You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2021/10/13 11:21:00 UTC

[skywalking-cli] branch master updated: fix: `multiple-linear` command's `labels` type can be string type (#122)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git


The following commit(s) were added to refs/heads/master by this push:
     new 6242ac4  fix: `multiple-linear` command's `labels` type can be string type (#122)
6242ac4 is described below

commit 6242ac45893dfed857cd68f7a1f350130a1d7371
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Wed Oct 13 19:20:55 2021 +0800

    fix: `multiple-linear` command's `labels` type can be string type (#122)
---
 assets/templates/dashboard/global.yml              |  6 ++--
 examples/global.yml                                |  6 ++--
 .../metrics/linear/multiple-linear-metrics.go      | 38 +++++++++++++++++++---
 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                    | 34 +++++++++++--------
 pkg/graphql/utils/adapter.go                       | 15 ++++-----
 test/expected/dashboard-global.yml                 |  5 +--
 9 files changed, 95 insertions(+), 65 deletions(-)

diff --git a/assets/templates/dashboard/global.yml b/assets/templates/dashboard/global.yml
index b9377a7..9b7d9f7 100644
--- a/assets/templates/dashboard/global.yml
+++ b/assets/templates/dashboard/global.yml
@@ -76,8 +76,8 @@ responseLatency:
     entity:
       scope: "All"
       normal: true
-  labels: "P50, P75, P90, P95, P99"
-  labelsIndex: "0, 1, 2, 3, 4"
+  relabels: "P50,P75,P90,P95,P99"
+  labels: "0,1,2,3,4"
   title: "Global Response Latency"
   unit: "percentile in ms"
 
@@ -89,4 +89,4 @@ heatMap:
       scope: "All"
       normal: true
   title: "Global Heatmap"
-  unit: "ms"
\ No newline at end of file
+  unit: "ms"
diff --git a/examples/global.yml b/examples/global.yml
index b9377a7..33e8615 100644
--- a/examples/global.yml
+++ b/examples/global.yml
@@ -76,8 +76,8 @@ responseLatency:
     entity:
       scope: "All"
       normal: true
-  labels: "P50, P75, P90, P95, P99"
-  labelsIndex: "0, 1, 2, 3, 4"
+  relabels: "P50, P75, P90, P95, P99"
+  labels: "0,1,2,3,4"
   title: "Global Response Latency"
   unit: "percentile in ms"
 
@@ -89,4 +89,4 @@ heatMap:
       scope: "All"
       normal: true
   title: "Global Heatmap"
-  unit: "ms"
\ No newline at end of file
+  unit: "ms"
diff --git a/internal/commands/metrics/linear/multiple-linear-metrics.go b/internal/commands/metrics/linear/multiple-linear-metrics.go
index af4a407..5e987e0 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,14 @@ $ 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 +78,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 +114,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..52a01c3 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
+	linears := make(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..3586de4 100644
--- a/pkg/graphql/dashboard/global.go
+++ b/pkg/graphql/dashboard/global.go
@@ -50,11 +50,11 @@ type MetricTemplate struct {
 }
 
 type ChartTemplate struct {
-	Condition   api.MetricsCondition `mapstructure:"condition"`
-	Title       string               `mapstructure:"title"`
-	Unit        string               `mapstructure:"unit"`
-	Labels      string               `mapstructure:"labels"`
-	LabelsIndex string               `mapstructure:"labelsIndex"`
+	Condition api.MetricsCondition `mapstructure:"condition"`
+	Title     string               `mapstructure:"title"`
+	Unit      string               `mapstructure:"unit"`
+	Relabels  string               `mapstructure:"relabels"`
+	Labels    string               `mapstructure:"labels"`
 }
 
 type GlobalTemplate struct {
@@ -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
@@ -175,18 +175,24 @@ func responseLatency(ctx *cli.Context, duration api.Duration) []map[string]float
 		return nil
 	}
 
-	// LabelsIndex in the template file is string type, like "0, 1, 2",
+	// Labels 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, ", ")
+	labels := strings.Split(template.ResponseLatency.Labels, ",")
+	relabels := strings.Split(template.ResponseLatency.Relabels, ",")
 
-	responseLatency, err := metrics.MultipleLinearIntValues(ctx, template.ResponseLatency.Condition, labelsIndex, duration)
+	responseLatency, err := metrics.MultipleLinearIntValues(ctx, template.ResponseLatency.Condition, labels, duration)
 
 	if err != nil {
 		logger.Log.Fatalln(err)
 	}
 
+	mapping := make(map[string]string, len(labels))
+	for i := 0; i < len(labels); i++ {
+		mapping[labels[i]] = relabels[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
 }
diff --git a/test/expected/dashboard-global.yml b/test/expected/dashboard-global.yml
index 5c51e06..6d57aee 100644
--- a/test/expected/dashboard-global.yml
+++ b/test/expected/dashboard-global.yml
@@ -28,8 +28,9 @@ metrics:
   {{- end }}
 
 responselatency:
-  {{- range .responselatency }}
-  - {{- range $k, $v := . }}
+  {{- range $k1, $v1 := .responselatency }}
+  {{ $k1 }}:
+    {{- range $k, $v := $v1 }}
     {{ $k }}: {{ $v }}
     {{- end }}
   {{- end }}