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 08:55:52 UTC

[skywalking-cli] branch bugfix/label created (now e96467d)

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

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


      at e96467d  fix: `multiple-linear` command's `labels` type can be string type

This branch includes the following new commits:

     new e96467d  fix: `multiple-linear` command's `labels` type can be string type

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[skywalking-cli] 01/01: fix: `multiple-linear` command's `labels` type can be string type

Posted by ke...@apache.org.
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 e96467df155308c7b1282d82d99b3b2176dc69cf
Author: kezhenxu94 <ke...@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..b14cbae 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`,
+				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
 }