Improve the presentation of the measurement listing from the CLI
This commit is contained in:
		
							parent
							
								
									251f136b53
								
							
						
					
					
						commit
						4ed94dfc53
					
				| @ -3,6 +3,7 @@ package main | ||||
| import ( | ||||
| 	// commands | ||||
| 
 | ||||
| 	"github.com/apex/log" | ||||
| 	_ "github.com/ooni/probe-cli/internal/cli/geoip" | ||||
| 	_ "github.com/ooni/probe-cli/internal/cli/info" | ||||
| 	_ "github.com/ooni/probe-cli/internal/cli/list" | ||||
| @ -19,5 +20,9 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	crashreport.CapturePanicAndWait(app.Run, nil) | ||||
| 	err, _ := crashreport.CapturePanic(app.Run, nil) | ||||
| 	if err != nil { | ||||
| 		log.WithError(err.(error)).Error("panic in app.Run") | ||||
| 		crashreport.Wait() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -27,27 +27,41 @@ func init() { | ||||
| 			} | ||||
| 
 | ||||
| 			msmtSummary := output.MeasurementSummaryData{ | ||||
| 				TotalCount:    0, | ||||
| 				AnomalyCount:  0, | ||||
| 				DataUsageUp:   0.0, | ||||
| 				DataUsageDown: 0.0, | ||||
| 				TotalRuntime:  0, | ||||
| 				TotalCount:         0, | ||||
| 				AnomalyCount:       0, | ||||
| 				DataUsageUp:        0.0, | ||||
| 				DataUsageDown:      0.0, | ||||
| 				TotalRuntime:       0, | ||||
| 				ASN:                0, | ||||
| 				NetworkName:        "", | ||||
| 				NetworkCountryCode: "ZZ", | ||||
| 			} | ||||
| 			for _, msmt := range measurements { | ||||
| 			isFirst := true | ||||
| 			isLast := false | ||||
| 			for idx, msmt := range measurements { | ||||
| 				if idx > 0 { | ||||
| 					isFirst = false | ||||
| 				} | ||||
| 				if idx == len(measurements)-1 { | ||||
| 					isLast = true | ||||
| 				} | ||||
| 
 | ||||
| 				// We assume that since these are summary level information the first | ||||
| 				// item will contain the information necessary. | ||||
| 				if msmtSummary.TotalRuntime == 0 { | ||||
| 				if isFirst { | ||||
| 					msmtSummary.TotalRuntime = msmt.ResultRuntime | ||||
| 				} | ||||
| 				if msmtSummary.DataUsageUp == 0 { | ||||
| 					msmtSummary.DataUsageUp = msmt.DataUsageUp | ||||
| 					msmtSummary.DataUsageDown = msmt.DataUsageDown | ||||
| 					msmtSummary.NetworkName = msmt.NetworkName | ||||
| 					msmtSummary.NetworkCountryCode = msmt.NetworkCountryCode | ||||
| 					msmtSummary.ASN = msmt.ASN | ||||
| 					msmtSummary.StartTime = msmt.MeasurementStartTime | ||||
| 				} | ||||
| 				if msmt.IsAnomaly.Bool == true { | ||||
| 					msmtSummary.AnomalyCount++ | ||||
| 				} | ||||
| 				msmtSummary.TotalCount++ | ||||
| 				output.MeasurementItem(msmt) | ||||
| 				output.MeasurementItem(msmt, isFirst, isLast) | ||||
| 			} | ||||
| 			output.MeasurementSummary(msmtSummary) | ||||
| 		} else { | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package crashreport | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/apex/log" | ||||
| 	"github.com/getsentry/raven-go" | ||||
| ) | ||||
| 
 | ||||
| @ -8,13 +9,15 @@ import ( | ||||
| // crash reporting logic a no-op. | ||||
| var Disabled = false | ||||
| 
 | ||||
| var client *raven.Client | ||||
| 
 | ||||
| // CapturePanic is a wrapper around raven.CapturePanic that becomes a noop if | ||||
| // `Disabled` is set to true. | ||||
| func CapturePanic(f func(), tags map[string]string) (interface{}, string) { | ||||
| 	if Disabled == true { | ||||
| 		return nil, "" | ||||
| 	} | ||||
| 	return raven.CapturePanic(f, tags) | ||||
| 	return client.CapturePanic(f, tags) | ||||
| } | ||||
| 
 | ||||
| // CapturePanicAndWait is a wrapper around raven.CapturePanicAndWait that becomes a noop if | ||||
| @ -23,7 +26,7 @@ func CapturePanicAndWait(f func(), tags map[string]string) (interface{}, string) | ||||
| 	if Disabled == true { | ||||
| 		return nil, "" | ||||
| 	} | ||||
| 	return raven.CapturePanicAndWait(f, tags) | ||||
| 	return client.CapturePanicAndWait(f, tags) | ||||
| } | ||||
| 
 | ||||
| // CaptureError is a wrapper around raven.CaptureError | ||||
| @ -31,7 +34,7 @@ func CaptureError(err error, tags map[string]string) string { | ||||
| 	if Disabled == true { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return raven.CaptureError(err, tags) | ||||
| 	return client.CaptureError(err, tags) | ||||
| } | ||||
| 
 | ||||
| // CaptureErrorAndWait is a wrapper around raven.CaptureErrorAndWait | ||||
| @ -39,9 +42,21 @@ func CaptureErrorAndWait(err error, tags map[string]string) string { | ||||
| 	if Disabled == true { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return raven.CaptureErrorAndWait(err, tags) | ||||
| 	return client.CaptureErrorAndWait(err, tags) | ||||
| } | ||||
| 
 | ||||
| // Wait will block on sending messages to the sentry server | ||||
| func Wait() { | ||||
| 	if Disabled == false { | ||||
| 		log.Info("sending exception backtrace") | ||||
| 		client.Wait() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	raven.SetDSN("https://cb4510e090f64382ac371040c19b2258:8448daeebfa643c289ef398f8645980b@sentry.io/1234954") | ||||
| 	var err error | ||||
| 	client, err = raven.NewClient("https://cb4510e090f64382ac371040c19b2258:8448daeebfa643c289ef398f8645980b@sentry.io/1234954", nil) | ||||
| 	if err != nil { | ||||
| 		log.WithError(err).Error("failed to create a raven client") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -114,6 +114,10 @@ func (h *Handler) TypedLog(t string, e *log.Entry) error { | ||||
| 		return nil | ||||
| 	case "table": | ||||
| 		return logTable(h.Writer, e.Fields) | ||||
| 	case "measurement_item": | ||||
| 		return logMeasurementItem(h.Writer, e.Fields) | ||||
| 	case "measurement_summary": | ||||
| 		return logMeasurementSummary(h.Writer, e.Fields) | ||||
| 	case "result_item": | ||||
| 		return logResultItem(h.Writer, e.Fields) | ||||
| 	case "result_summary": | ||||
|  | ||||
							
								
								
									
										103
									
								
								internal/log/handlers/cli/measurements.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								internal/log/handlers/cli/measurements.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/apex/log" | ||||
| 	"github.com/ooni/probe-cli/internal/util" | ||||
| ) | ||||
| 
 | ||||
| func statusIcon(ok bool) string { | ||||
| 	if ok { | ||||
| 		return "✓" | ||||
| 	} | ||||
| 	return "❌" | ||||
| } | ||||
| func logMeasurementItem(w io.Writer, f log.Fields) error { | ||||
| 	colWidth := 24 | ||||
| 
 | ||||
| 	rID := f.Get("id").(int64) | ||||
| 	testName := f.Get("test_name").(string) | ||||
| 
 | ||||
| 	// We currently don't use these fields in the view | ||||
| 	//testGroupName := f.Get("test_group_name").(string) | ||||
| 	//networkName := f.Get("network_name").(string) | ||||
| 	//asn := fmt.Sprintf("AS%d (%s)", f.Get("asn").(uint), f.Get("network_country_code").(string)) | ||||
| 	testKeys := f.Get("test_keys").(string) | ||||
| 
 | ||||
| 	isAnomaly := f.Get("is_anomaly").(bool) | ||||
| 	isFailed := f.Get("is_failed").(bool) | ||||
| 	isUploaded := f.Get("is_uploaded").(bool) | ||||
| 	url := f.Get("url").(string) | ||||
| 	urlCategoryCode := f.Get("url_category_code").(string) | ||||
| 
 | ||||
| 	isFirst := f.Get("is_first").(bool) | ||||
| 	isLast := f.Get("is_last").(bool) | ||||
| 	if isFirst { | ||||
| 		fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth*2+2)+"┓\n") | ||||
| 	} else { | ||||
| 		fmt.Fprintf(w, "┢"+strings.Repeat("━", colWidth*2+2)+"┪\n") | ||||
| 	} | ||||
| 
 | ||||
| 	anomalyStr := fmt.Sprintf("ok: %s", statusIcon(!isAnomaly)) | ||||
| 	uploadStr := fmt.Sprintf("uploaded: %s", statusIcon(isUploaded)) | ||||
| 	failureStr := fmt.Sprintf("success: %s", statusIcon(!isFailed)) | ||||
| 
 | ||||
| 	fmt.Fprintf(w, fmt.Sprintf("│ %s │\n", | ||||
| 		util.RightPad( | ||||
| 			fmt.Sprintf("#%d", rID), colWidth*2))) | ||||
| 
 | ||||
| 	if url != "" { | ||||
| 		fmt.Fprintf(w, fmt.Sprintf("│ %s │\n", | ||||
| 			util.RightPad( | ||||
| 				fmt.Sprintf("%s (%s)", url, urlCategoryCode), colWidth*2))) | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n", | ||||
| 		util.RightPad(testName, colWidth), | ||||
| 		util.RightPad(anomalyStr, colWidth))) | ||||
| 
 | ||||
| 	fmt.Fprintf(w, fmt.Sprintf("│ %s │\n", | ||||
| 		util.RightPad(testKeys, colWidth*2))) | ||||
| 	fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n", | ||||
| 		util.RightPad(failureStr, colWidth), | ||||
| 		util.RightPad(uploadStr, colWidth))) | ||||
| 
 | ||||
| 	if isLast { | ||||
| 		fmt.Fprintf(w, "└┬────────────────────────────────────────────────┬┘\n") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func logMeasurementSummary(w io.Writer, f log.Fields) error { | ||||
| 	colWidth := 12 | ||||
| 
 | ||||
| 	totalCount := f.Get("total_count").(int64) | ||||
| 	anomalyCount := f.Get("anomaly_count").(int64) | ||||
| 	totalRuntime := f.Get("total_runtime").(float64) | ||||
| 	dataUp := f.Get("data_usage_up").(float64) | ||||
| 	dataDown := f.Get("data_usage_down").(float64) | ||||
| 
 | ||||
| 	startTime := f.Get("start_time").(time.Time) | ||||
| 
 | ||||
| 	asn := f.Get("asn").(uint) | ||||
| 	countryCode := f.Get("network_country_code").(string) | ||||
| 	networkName := f.Get("network_name").(string) | ||||
| 
 | ||||
| 	fmt.Fprintf(w, " │ %s  │\n", | ||||
| 		util.RightPad(startTime.Format(time.RFC822), (colWidth+3)*3), | ||||
| 	) | ||||
| 	fmt.Fprintf(w, " │ %s  │\n", | ||||
| 		util.RightPad(fmt.Sprintf("AS%d, %s (%s)", asn, networkName, countryCode), (colWidth+3)*3), | ||||
| 	) | ||||
| 	fmt.Fprintf(w, " │ %s   %s   %s │\n", | ||||
| 		util.RightPad(fmt.Sprintf("%.2fs", totalRuntime), colWidth), | ||||
| 		util.RightPad(fmt.Sprintf("%d/%d anmls", anomalyCount, totalCount), colWidth), | ||||
| 		util.RightPad(fmt.Sprintf("⬆ %s  ⬇ %s", formatSize(dataUp), formatSize(dataDown)), colWidth+4)) | ||||
| 	fmt.Fprintf(w, " └────────────────────────────────────────────────┘\n") | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| @ -126,7 +126,7 @@ func Onboarding(config *config.Config) error { | ||||
| 
 | ||||
| 	config.Lock() | ||||
| 	config.InformedConsent = true | ||||
| 	config.Advanced.IncludeCountry = settings.IncludeCountry | ||||
| 	config.Sharing.IncludeCountry = settings.IncludeCountry | ||||
| 	config.Advanced.SendCrashReports = settings.SendCrashReports | ||||
| 	config.Sharing.IncludeIP = settings.IncludeIP | ||||
| 	config.Sharing.IncludeASN = settings.IncludeNetwork | ||||
|  | ||||
| @ -21,11 +21,15 @@ func Progress(key string, perc float64, msg string) { | ||||
| } | ||||
| 
 | ||||
| type MeasurementSummaryData struct { | ||||
| 	TotalRuntime  float64 | ||||
| 	TotalCount    int64 | ||||
| 	AnomalyCount  int64 | ||||
| 	DataUsageUp   float64 | ||||
| 	DataUsageDown float64 | ||||
| 	TotalRuntime       float64 | ||||
| 	TotalCount         int64 | ||||
| 	AnomalyCount       int64 | ||||
| 	DataUsageUp        float64 | ||||
| 	DataUsageDown      float64 | ||||
| 	ASN                uint | ||||
| 	NetworkName        string | ||||
| 	NetworkCountryCode string | ||||
| 	StartTime          time.Time | ||||
| } | ||||
| 
 | ||||
| func MeasurementSummary(msmt MeasurementSummaryData) { | ||||
| @ -36,13 +40,20 @@ func MeasurementSummary(msmt MeasurementSummaryData) { | ||||
| 		"anomaly_count":   msmt.AnomalyCount, | ||||
| 		"data_usage_down": msmt.DataUsageDown, | ||||
| 		"data_usage_up":   msmt.DataUsageUp, | ||||
| 		"asn":             msmt.ASN, | ||||
| 		"network_country_code": msmt.NetworkCountryCode, | ||||
| 		"network_name":         msmt.NetworkName, | ||||
| 		"start_time":           msmt.StartTime, | ||||
| 	}).Info("measurement summary") | ||||
| } | ||||
| 
 | ||||
| // MeasurementItem logs a progress type event | ||||
| func MeasurementItem(msmt database.MeasurementURLNetwork) { | ||||
| func MeasurementItem(msmt database.MeasurementURLNetwork, isFirst bool, isLast bool) { | ||||
| 	log.WithFields(log.Fields{ | ||||
| 		"type":                 "measurement_item", | ||||
| 		"type":     "measurement_item", | ||||
| 		"is_first": isFirst, | ||||
| 		"is_last":  isLast, | ||||
| 
 | ||||
| 		"id":                   msmt.MsmtTblID, | ||||
| 		"test_name":            msmt.TestName, | ||||
| 		"test_group_name":      msmt.Result.TestGroupName, | ||||
|  | ||||
| @ -120,7 +120,7 @@ func (c *Controller) Init(nt *mk.Nettest) error { | ||||
| 	nt.Options = mk.NettestOptions{ | ||||
| 		IncludeIP:      c.Ctx.Config.Sharing.IncludeIP, | ||||
| 		IncludeASN:     c.Ctx.Config.Sharing.IncludeASN, | ||||
| 		IncludeCountry: c.Ctx.Config.Advanced.IncludeCountry, | ||||
| 		IncludeCountry: c.Ctx.Config.Sharing.IncludeCountry, | ||||
| 		LogLevel:       "DEBUG", | ||||
| 
 | ||||
| 		ProbeCC:  c.Ctx.Location.CountryCode, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user