Skip to content

Commit

Permalink
fix: multiple-linear command's labels type can be string type (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
kezhenxu94 authored Oct 13, 2021
1 parent 515a6a4 commit 6242ac4
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 65 deletions.
6 changes: 3 additions & 3 deletions assets/templates/dashboard/global.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -89,4 +89,4 @@ heatMap:
scope: "All"
normal: true
title: "Global Heatmap"
unit: "ms"
unit: "ms"
6 changes: 3 additions & 3 deletions examples/global.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -89,4 +89,4 @@ heatMap:
scope: "All"
normal: true
title: "Global Heatmap"
unit: "ms"
unit: "ms"
38 changes: 33 additions & 5 deletions internal/commands/metrics/linear/multiple-linear-metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
},
},
),
Expand All @@ -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
Expand All @@ -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})
},
}
14 changes: 5 additions & 9 deletions pkg/display/graph/dashboard/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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{
Expand Down
10 changes: 3 additions & 7 deletions pkg/display/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package graph
import (
"fmt"
"reflect"
"strings"

api "skywalking.apache.org/repo/goapi/query"

Expand All @@ -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
Expand All @@ -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

Expand All @@ -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)))
Expand Down
32 changes: 19 additions & 13 deletions pkg/display/graph/linear/linear.go
Original file line number Diff line number Diff line change
Expand Up @@ -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++ {
Expand All @@ -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),
Expand All @@ -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
Expand All @@ -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
}
Expand Down
34 changes: 20 additions & 14 deletions pkg/graphql/dashboard/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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()
Expand Down
15 changes: 6 additions & 9 deletions pkg/graphql/utils/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
package utils

import (
"strconv"
"strings"
"time"

api "skywalking.apache.org/repo/goapi/query"
Expand All @@ -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
}
Expand Down
Loading

0 comments on commit 6242ac4

Please sign in to comment.