This is an automated email from the ASF dual-hosted git repository. kezhenxu94 pushed a commit to branch bugfix/label in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git
commit 1e82b663d9dc7f4f9ecb8133cf0b5bbbde847cd6 Author: kezhenxu94 <kezhenx...@apache.org> AuthorDate: Wed Oct 13 16:55:45 2021 +0800 fix: `multiple-linear` command's `labels` type can be string type --- .../metrics/linear/multiple-linear-metrics.go | 37 +++++++++++++++++++--- pkg/display/graph/dashboard/global.go | 14 +++----- pkg/display/graph/graph.go | 10 ++---- pkg/display/graph/linear/linear.go | 32 +++++++++++-------- pkg/graphql/dashboard/global.go | 18 +++++++---- pkg/graphql/utils/adapter.go | 15 ++++----- 6 files changed, 77 insertions(+), 49 deletions(-) diff --git a/internal/commands/metrics/linear/multiple-linear-metrics.go b/internal/commands/metrics/linear/multiple-linear-metrics.go index af4a407..8ac636b 100644 --- a/internal/commands/metrics/linear/multiple-linear-metrics.go +++ b/internal/commands/metrics/linear/multiple-linear-metrics.go @@ -41,7 +41,11 @@ var Multiple = &cli.Command{ Examples: 1. Query the global percentiles: -$ swctl metrics multiple-linear --name all_percentile`, +$ swctl metrics multiple-linear --name all_percentile + +2. Relabel the labels for better readability: +$ swctl metrics multiple-linear --name all_percentile --labels=0,1,2,3,4 --relabels=P50,P75,P90,P95,P99 +`, Flags: flags.Flags( flags.DurationFlags, flags.MetricsFlags, @@ -52,7 +56,13 @@ $ swctl metrics multiple-linear --name all_percentile`, Name: "labels", Usage: "the labels you need to query", Required: false, - Value: "0,1,2,3,4", + }, + }, + []cli.Flag{ + &cli.StringFlag{ + Name: "relabels", + Usage: `the new labels to map to the original "--labels", must be in same size and is order-sensitive. "labels[i]" will be mapped to "relabels[i]"`, + Required: false, }, }, ), @@ -67,7 +77,24 @@ $ swctl metrics multiple-linear --name all_percentile`, step := ctx.Generic("step") metricsName := ctx.String("name") - labels := ctx.String("labels") + labelsString := ctx.String("labels") + relabelsString := ctx.String("relabels") + + labels := strings.Split(labelsString, ",") + relabels := strings.Split(relabelsString, ",") + + labelMapping := make(map[string]string) + switch { + case labelsString == "" && relabelsString != "": + return fmt.Errorf(`"--labels" cannot be empty when "--relabels" is given`) + case labelsString != "" && relabelsString != "" && len(labels) != len(relabels): + return fmt.Errorf(`"--labels" and "--relabels" must be in same size if both specified, but was %v != %v`, len(labels), len(relabels)) + case relabelsString != "": + for i := 0; i < len(labels); i++ { + labelMapping[labels[i]] = relabels[i] + } + } + entity, err := interceptor.ParseEntity(ctx) if err != nil { return err @@ -86,13 +113,13 @@ $ swctl metrics multiple-linear --name all_percentile`, metricsValuesArray, err := metrics.MultipleLinearIntValues(ctx, api.MetricsCondition{ Name: metricsName, Entity: entity, - }, strings.Split(labels, ","), duration) + }, labels, duration) if err != nil { return err } - reshaped := utils.MetricsValuesArrayToMap(duration, metricsValuesArray) + reshaped := utils.MetricsValuesArrayToMap(duration, metricsValuesArray, labelMapping) return display.Display(ctx, &displayable.Displayable{Data: reshaped}) }, } diff --git a/pkg/display/graph/dashboard/global.go b/pkg/display/graph/dashboard/global.go index 97c1801..6e2be86 100644 --- a/pkg/display/graph/dashboard/global.go +++ b/pkg/display/graph/dashboard/global.go @@ -75,16 +75,13 @@ var strToLayoutType = map[string]layoutType{ // widgets holds the widgets used by the dashboard. type widgets struct { gauges []*gauge.MetricColumn - linears []*linechart.LineChart + linears map[string]*linechart.LineChart heatmap *lib.HeatMap // buttons are used to change the layout. buttons []*button.Button } -// linearTitles are titles of each line chart, load from the template file. -var linearTitles []string - // template determines how the global dashboard is displayed. var template *dashboard.GlobalTemplate @@ -164,7 +161,7 @@ func gridLayout(lt layoutType) ([]container.Option, error) { ) case layoutLineChart: - lcElements := linear.LineChartElements(allWidgets.linears, linearTitles) + lcElements := linear.LineChartElements(allWidgets.linears) percentage := int(math.Min(99, float64((100-buttonRowHeight)/len(lcElements)))) for _, e := range lcElements { @@ -200,7 +197,7 @@ func gridLayout(lt layoutType) ([]container.Option, error) { // newWidgets creates all widgets used by the dashboard. func newWidgets(data *dashboard.GlobalData) error { var columns []*gauge.MetricColumn - var linears []*linechart.LineChart + var linears map[string]*linechart.LineChart // Create gauges to display global metrics. for i := range template.Metrics { @@ -212,12 +209,12 @@ func newWidgets(data *dashboard.GlobalData) error { } // Create line charts to display global response latency. - for _, input := range data.ResponseLatency { + for label, input := range data.ResponseLatency { l, err := linear.NewLineChart(input) if err != nil { return err } - linears = append(linears, l) + linears[label] = l } // Create a heat map. @@ -253,7 +250,6 @@ func Display(ctx *cli.Context, data *dashboard.GlobalData) error { return err } template = te - linearTitles = strings.Split(template.ResponseLatency.Labels, ", ") // Initialization allWidgets = &widgets{ diff --git a/pkg/display/graph/graph.go b/pkg/display/graph/graph.go index 8334048..4f9db13 100644 --- a/pkg/display/graph/graph.go +++ b/pkg/display/graph/graph.go @@ -20,7 +20,6 @@ package graph import ( "fmt" "reflect" - "strings" api "skywalking.apache.org/repo/goapi/query" @@ -38,7 +37,7 @@ import ( type ( Thermodynamic = api.HeatMap LinearMetrics = map[string]float64 - MultiLinearMetrics = []LinearMetrics + MultiLinearMetrics = map[string]LinearMetrics Trace = api.Trace TraceBrief = api.TraceBrief GlobalMetrics = [][]*api.SelectedRecord @@ -55,8 +54,6 @@ var ( GlobalDataType = reflect.TypeOf(&GlobalData{}) ) -const multipleLinearTitles = "P50, P75, P90, P95, P99" - func Display(ctx *cli.Context, displayable *d.Displayable) error { data := displayable.Data @@ -65,12 +62,11 @@ func Display(ctx *cli.Context, displayable *d.Displayable) error { return heatmap.Display(displayable) case LinearMetricsType: - return linear.Display(ctx, []LinearMetrics{data.(LinearMetrics)}, nil) + return linear.Display(ctx, map[string]LinearMetrics{"": data.(LinearMetrics)}) case MultiLinearMetricsType: inputs := data.(MultiLinearMetrics) - titles := strings.Split(multipleLinearTitles, ", ")[:len(inputs)] - return linear.Display(ctx, inputs, titles) + return linear.Display(ctx, inputs) case TraceType: return tree.Display(tree.Adapt(data.(Trace))) diff --git a/pkg/display/graph/linear/linear.go b/pkg/display/graph/linear/linear.go index 5dc443f..0186b4d 100644 --- a/pkg/display/graph/linear/linear.go +++ b/pkg/display/graph/linear/linear.go @@ -78,11 +78,21 @@ func processInputs(inputs map[string]float64) (xLabels map[int]string, yValues [ // LineChartElements is the part that separated from layout, // which can be reused by global dashboard. -func LineChartElements(lineCharts []*linechart.LineChart, titles []string) [][]grid.Element { +func LineChartElements(lineCharts map[string]*linechart.LineChart) [][]grid.Element { cols := maxSqrt(len(lineCharts)) rows := make([][]grid.Element, int(math.Ceil(float64(len(lineCharts))/float64(cols)))) + var charts []*linechart.LineChart + var titles []string + for t := range lineCharts { + titles = append(titles, t) + } + sort.Strings(titles) + for _, title := range titles { + charts = append(charts, lineCharts[title]) + } + for r := 0; r < len(rows); r++ { var row []grid.Element for c := 0; c < cols && r*cols+c < len(lineCharts); c++ { @@ -91,17 +101,13 @@ func LineChartElements(lineCharts []*linechart.LineChart, titles []string) [][]g percentage = int(math.Floor(float64(100) / float64(len(lineCharts)-r*cols))) } - var title string - if titles == nil { - title = fmt.Sprintf("#%v", r*cols+c) - } else { - title = titles[r*cols+c] - } + title := titles[r*cols+c] + chart := charts[r*cols+c] row = append(row, grid.ColWidthPerc( int(math.Min(99, float64(percentage))), grid.Widget( - lineCharts[r*cols+c], + chart, container.Border(linestyle.Light), container.BorderTitleAlignCenter(), container.BorderTitle(title), @@ -125,7 +131,7 @@ func layout(rows [][]grid.Element) ([]container.Option, error) { return builder.Build() } -func Display(cliCtx *cli.Context, inputs []map[string]float64, titles []string) error { +func Display(cliCtx *cli.Context, inputs map[string]map[string]float64) error { t, err := termbox.New() if err != nil { return err @@ -140,17 +146,17 @@ func Display(cliCtx *cli.Context, inputs []map[string]float64, titles []string) return err } - var elements []*linechart.LineChart + elements := make(map[string]*linechart.LineChart) - for _, input := range inputs { + for title, input := range inputs { w, e := NewLineChart(input) if e != nil { return e } - elements = append(elements, w) + elements[title] = w } - gridOpts, err := layout(LineChartElements(elements, titles)) + gridOpts, err := layout(LineChartElements(elements)) if err != nil { return err } diff --git a/pkg/graphql/dashboard/global.go b/pkg/graphql/dashboard/global.go index eb0b2fc..3a4fc96 100644 --- a/pkg/graphql/dashboard/global.go +++ b/pkg/graphql/dashboard/global.go @@ -65,9 +65,9 @@ type GlobalTemplate struct { } type GlobalData struct { - Metrics [][]*api.SelectedRecord `json:"metrics"` - ResponseLatency []map[string]float64 `json:"responseLatency"` - HeatMap api.HeatMap `json:"heatMap"` + Metrics [][]*api.SelectedRecord `json:"metrics"` + ResponseLatency map[string]map[string]float64 `json:"responseLatency"` + HeatMap api.HeatMap `json:"heatMap"` } // Use singleton pattern to make sure to load template only once. @@ -164,7 +164,7 @@ func Metrics(ctx *cli.Context, duration api.Duration) ([][]*api.SelectedRecord, return ret, nil } -func responseLatency(ctx *cli.Context, duration api.Duration) []map[string]float64 { +func responseLatency(ctx *cli.Context, duration api.Duration) map[string]map[string]float64 { template, err := LoadTemplate(ctx.String("template")) if err != nil { return nil @@ -178,6 +178,7 @@ func responseLatency(ctx *cli.Context, duration api.Duration) []map[string]float // LabelsIndex in the template file is string type, like "0, 1, 2", // need use ", " to split into string array for graphql query. labelsIndex := strings.Split(template.ResponseLatency.LabelsIndex, ", ") + newLabels := strings.Split(template.ResponseLatency.Labels, ",") responseLatency, err := metrics.MultipleLinearIntValues(ctx, template.ResponseLatency.Condition, labelsIndex, duration) @@ -185,8 +186,13 @@ func responseLatency(ctx *cli.Context, duration api.Duration) []map[string]float logger.Log.Fatalln(err) } + mapping := make(map[string]string, len(labelsIndex)) + for i := 0; i < len(labelsIndex); i++ { + mapping[labelsIndex[i]] = newLabels[i] + } + // Convert metrics values to map type data. - return utils.MetricsValuesArrayToMap(duration, responseLatency) + return utils.MetricsValuesArrayToMap(duration, responseLatency, mapping) } func heatMap(ctx *cli.Context, duration api.Duration) (api.HeatMap, error) { @@ -224,7 +230,7 @@ func Global(ctx *cli.Context, duration api.Duration) (*GlobalData, error) { } wg.Done() }() - var rl []map[string]float64 + var rl map[string]map[string]float64 go func() { rl = responseLatency(ctx, duration) wg.Done() diff --git a/pkg/graphql/utils/adapter.go b/pkg/graphql/utils/adapter.go index 129d902..f8af462 100644 --- a/pkg/graphql/utils/adapter.go +++ b/pkg/graphql/utils/adapter.go @@ -18,8 +18,6 @@ package utils import ( - "strconv" - "strings" "time" api "skywalking.apache.org/repo/goapi/query" @@ -28,15 +26,14 @@ import ( ) // MetricsValuesArrayToMap converts Array of MetricsValues into a map that uses time as key. -func MetricsValuesArrayToMap(duration api.Duration, mvArray []api.MetricsValues) []map[string]float64 { - ret := make([]map[string]float64, len(mvArray)) +func MetricsValuesArrayToMap(duration api.Duration, mvArray []api.MetricsValues, labelsMap map[string]string) map[string]map[string]float64 { + ret := make(map[string]map[string]float64, len(mvArray)) for _, mvs := range mvArray { - index, err := strconv.Atoi(strings.TrimSpace(*mvs.Label)) - if err != nil { - logger.Log.Fatalln(err) - return nil + label := *mvs.Label + if l, ok := labelsMap[label]; ok { + label = l } - ret[index] = MetricsValuesToMap(duration, mvs) + ret[label] = MetricsValuesToMap(duration, mvs) } return ret }