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 6ae6ca0 Add a subcommand of `swctl dashboard global-metrics` (#46) 6ae6ca0 is described below commit 6ae6ca04ba7cc47c9f69d7d34cb325f4723cf588 Author: Hoshea Jiang <fgk...@gmail.com> AuthorDate: Mon Jul 27 21:47:33 2020 +0800 Add a subcommand of `swctl dashboard global-metrics` (#46) --- README.md | 16 ++ assets/graphqls/dashboard/HeatMap.graphql | 29 +++ .../dashboard/LabeledMetricsValues.graphql | 27 +++ assets/graphqls/dashboard/SortMetrics.graphql | 23 +++ assets/templates/Dashboard.Global.json | 69 +++++++ cmd/main.go | 3 + .../display.go => commands/dashboard/dashboard.go | 44 +---- commands/dashboard/global/global.go | 57 ++++++ commands/dashboard/global/metrics.go | 63 ++++++ display/display.go | 2 +- display/graph/gauge/gauge.go | 218 +++++++++++++++++++++ display/graph/graph.go | 11 +- example/Dashboard.Global.json | 69 +++++++ graphql/dashboard/global.go | 137 +++++++++++++ 14 files changed, 732 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 22eef04..3b6de38 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,22 @@ Ascii Graph, like coloring in terminal, so please use `json` or `yaml` instead. </details> +### `dashboard` + +<details> + +<summary>dashboard global-metrics [--template=template]</summary> + +`dashboard global-metrics` displays global metrics in the form of a dashboard. + +| argument | description | default | +| :--- | :--- | :--- | +| `--template` | the template file to customize how to display information | `templates/Dashboard.Global.json` | + +You can imitate the content of [the default template file](example/Dashboard.Global.json) to customize the dashboard. + +</details> + # Use Cases <details> diff --git a/assets/graphqls/dashboard/HeatMap.graphql b/assets/graphqls/dashboard/HeatMap.graphql new file mode 100644 index 0000000..ddbbe81 --- /dev/null +++ b/assets/graphqls/dashboard/HeatMap.graphql @@ -0,0 +1,29 @@ +# Licensed to Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Apache Software Foundation (ASF) licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +query ($condition: MetricsCondition!, $duration: Duration!) { + result: readHeatMap(condition: $condition, duration: $duration) { + values { + id + values + } + buckets { + min + max + } + } +} diff --git a/assets/graphqls/dashboard/LabeledMetricsValues.graphql b/assets/graphqls/dashboard/LabeledMetricsValues.graphql new file mode 100644 index 0000000..9b8c2ed --- /dev/null +++ b/assets/graphqls/dashboard/LabeledMetricsValues.graphql @@ -0,0 +1,27 @@ +# Licensed to Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Apache Software Foundation (ASF) licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +query ($condition: MetricsCondition!, $label: [String!]!, $duration: Duration!) { + result: readLabeledMetricsValues(condition: $condition, labels: $label, duration: $duration) { + label + values { + values { + value + } + } + } +} diff --git a/assets/graphqls/dashboard/SortMetrics.graphql b/assets/graphqls/dashboard/SortMetrics.graphql new file mode 100644 index 0000000..051aff5 --- /dev/null +++ b/assets/graphqls/dashboard/SortMetrics.graphql @@ -0,0 +1,23 @@ +# Licensed to Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Apache Software Foundation (ASF) licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +query ($condition:TopNCondition!, $duration: Duration!) { + result: sortMetrics(condition: $condition, duration: $duration) { + name + value + } +} diff --git a/assets/templates/Dashboard.Global.json b/assets/templates/Dashboard.Global.json new file mode 100644 index 0000000..74e55ba --- /dev/null +++ b/assets/templates/Dashboard.Global.json @@ -0,0 +1,69 @@ +{ + "metrics": [ + { + "condition": { + "name": "service_cpm", + "normal": true, + "scope": "Service", + "topN": 10, + "order": "DES" + }, + "title": " Service Load (calls/min) " + }, + { + "condition": { + "name": "service_resp_time", + "normal": true, + "scope": "Service", + "topN": 10, + "order": "DES" + }, + "title": " Slow Services (ms) " + }, + { + "condition": { + "name": "service_apdex", + "normal": true, + "scope": "Service", + "topN": 10, + "order": "ASC" + }, + "title": "Un-Health Services (Apdex)", + "aggregation": "/", + "aggregationNum": "10000" + }, + { + "condition": { + "name": "endpoint_avg", + "normal": true, + "scope": "Endpoint", + "topN": 10, + "order": "DES" + }, + "title": " Slow Endpoints (ms) " + } + ], + "responseLatency": { + "condition": { + "name": "all_percentile", + "entity": { + "scope": "All", + "normal": true + } + }, + "labels": "0, 1, 2, 3, 4", + "title": "Global Response Latency", + "unit": "percentile in ms" + }, + "heatMap": { + "condition": { + "name": "all_heatmap", + "entity": { + "scope": "All", + "normal": true + } + }, + "title": "Global Heatmap", + "unit": "ms" + } +} diff --git a/cmd/main.go b/cmd/main.go index 74ec77e..40e81ae 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -24,6 +24,8 @@ import ( "github.com/apache/skywalking-cli/commands/common" "github.com/apache/skywalking-cli/commands/trace" + "github.com/apache/skywalking-cli/commands/dashboard" + "github.com/apache/skywalking-cli/commands/metrics" "github.com/apache/skywalking-cli/commands/endpoint" @@ -89,6 +91,7 @@ func main() { metrics.Command, trace.Command, common.Command, + dashboard.Command, } app.Before = interceptor.BeforeChain([]cli.BeforeFunc{ diff --git a/display/display.go b/commands/dashboard/dashboard.go similarity index 50% copy from display/display.go copy to commands/dashboard/dashboard.go index af3a598..cb1c7c7 100644 --- a/display/display.go +++ b/commands/dashboard/dashboard.go @@ -15,44 +15,20 @@ // specific language governing permissions and limitations // under the License. -package display +package dashboard import ( - "fmt" - "strings" - - d "github.com/apache/skywalking-cli/display/displayable" - - "github.com/apache/skywalking-cli/display/graph" - "github.com/urfave/cli" - "github.com/apache/skywalking-cli/display/json" - "github.com/apache/skywalking-cli/display/table" - "github.com/apache/skywalking-cli/display/yaml" + "github.com/apache/skywalking-cli/commands/dashboard/global" ) -const ( - JSON = "json" - YAML = "yaml" - TABLE = "table" - GRAPH = "graph" -) - -// Display the object in the style specified in flag --display -func Display(ctx *cli.Context, displayable *d.Displayable) error { - displayStyle := ctx.GlobalString("display") - - switch strings.ToLower(displayStyle) { - case JSON: - return json.Display(displayable) - case YAML: - return yaml.Display(displayable) - case TABLE: - return table.Display(displayable) - case GRAPH: - return graph.Display(displayable) - default: - return fmt.Errorf("unsupported display style: %s", displayStyle) - } +var Command = cli.Command{ + Name: "dashboard", + ShortName: "db", + Usage: "Dashboard related sub-command", + Subcommands: cli.Commands{ + global.GlobalCommand, + global.Metrics, + }, } diff --git a/commands/dashboard/global/global.go b/commands/dashboard/global/global.go new file mode 100644 index 0000000..2a1bc04 --- /dev/null +++ b/commands/dashboard/global/global.go @@ -0,0 +1,57 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package global + +import ( + "github.com/urfave/cli" + + "github.com/apache/skywalking-cli/commands/model" + "github.com/apache/skywalking-cli/graphql/schema" + + "github.com/apache/skywalking-cli/display/displayable" + + "github.com/apache/skywalking-cli/commands/flags" + "github.com/apache/skywalking-cli/commands/interceptor" + "github.com/apache/skywalking-cli/display" + "github.com/apache/skywalking-cli/graphql/dashboard" +) + +var GlobalCommand = cli.Command{ + Name: "global", + ShortName: "g", + Usage: "Display global data", + Description: "Display global data", + Flags: flags.DurationFlags, + Before: interceptor.BeforeChain([]cli.BeforeFunc{ + interceptor.TimezoneInterceptor, + interceptor.DurationInterceptor, + }), + Action: func(ctx *cli.Context) error { + end := ctx.String("end") + start := ctx.String("start") + step := ctx.Generic("step") + + globalData := dashboard.Global(ctx, schema.Duration{ + Start: start, + End: end, + Step: step.(*model.StepEnumValue).Selected, + }) + + return display.Display(ctx, &displayable.Displayable{Data: globalData}) + }, +} diff --git a/commands/dashboard/global/metrics.go b/commands/dashboard/global/metrics.go new file mode 100644 index 0000000..d5ffa40 --- /dev/null +++ b/commands/dashboard/global/metrics.go @@ -0,0 +1,63 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package global + +import ( + "github.com/apache/skywalking-cli/commands/flags" + "github.com/apache/skywalking-cli/commands/interceptor" + "github.com/apache/skywalking-cli/commands/model" + "github.com/apache/skywalking-cli/display" + "github.com/apache/skywalking-cli/display/displayable" + "github.com/apache/skywalking-cli/graphql/dashboard" + "github.com/apache/skywalking-cli/graphql/schema" + + "github.com/urfave/cli" +) + +var Metrics = cli.Command{ + Name: "global-metrics", + Usage: "Query global metrics", + Flags: flags.Flags( + flags.DurationFlags, + []cli.Flag{ + cli.StringFlag{ + Name: "template", + Usage: "load dashboard UI template", + Required: false, + Value: dashboard.DefaultTemplatePath, + }, + }, + ), + Before: interceptor.BeforeChain([]cli.BeforeFunc{ + interceptor.TimezoneInterceptor, + interceptor.DurationInterceptor, + }), + Action: func(ctx *cli.Context) error { + end := ctx.String("end") + start := ctx.String("start") + step := ctx.Generic("step") + + globalMetrics := dashboard.Metrics(ctx, schema.Duration{ + Start: start, + End: end, + Step: step.(*model.StepEnumValue).Selected, + }) + + return display.Display(ctx, &displayable.Displayable{Data: globalMetrics}) + }, +} diff --git a/display/display.go b/display/display.go index af3a598..9e7d132 100644 --- a/display/display.go +++ b/display/display.go @@ -51,7 +51,7 @@ func Display(ctx *cli.Context, displayable *d.Displayable) error { case TABLE: return table.Display(displayable) case GRAPH: - return graph.Display(displayable) + return graph.Display(ctx, displayable) default: return fmt.Errorf("unsupported display style: %s", displayStyle) } diff --git a/display/graph/gauge/gauge.go b/display/graph/gauge/gauge.go new file mode 100644 index 0000000..4edf51c --- /dev/null +++ b/display/graph/gauge/gauge.go @@ -0,0 +1,218 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package gauge + +import ( + "context" + "fmt" + "math" + "strconv" + "strings" + + "github.com/urfave/cli" + + "github.com/apache/skywalking-cli/graphql/dashboard" + "github.com/apache/skywalking-cli/graphql/schema" + + "github.com/mum4k/termdash" + "github.com/mum4k/termdash/cell" + "github.com/mum4k/termdash/container" + "github.com/mum4k/termdash/container/grid" + "github.com/mum4k/termdash/linestyle" + "github.com/mum4k/termdash/terminal/termbox" + "github.com/mum4k/termdash/terminal/terminalapi" + "github.com/mum4k/termdash/widgets/gauge" + "github.com/mum4k/termdash/widgets/text" +) + +const RootID = "root" + +type metricColumn struct { + title *text.Text + gauges []*gauge.Gauge +} + +func newMetricColumn(column []*schema.SelectedRecord, config *dashboard.MetricTemplate) (*metricColumn, error) { + var ret metricColumn + var maxValue int + + t, err := text.New() + if err != nil { + return nil, err + } + if err := t.Write(config.Title, text.WriteCellOpts(cell.FgColor(cell.ColorRed))); err != nil { + return nil, err + } + ret.title = t + + if config.Condition.Order == schema.OrderDes { + temp, err := strconv.Atoi(*(column[0].Value)) + if err != nil { + return nil, err + } + maxValue = temp + } else if config.Condition.Order == schema.OrderAsc { + temp, err := strconv.Atoi(*(column[len(column)-1].Value)) + if err != nil { + return nil, err + } + maxValue = temp + } + + for _, item := range column { + strValue := *(item.Value) + v, err := strconv.Atoi(strValue) + if err != nil { + return nil, err + } + + if config.AggregationNum != "" { + aggregationNum, convErr := strconv.Atoi(config.AggregationNum) + if convErr != nil { + return nil, convErr + } + strValue = fmt.Sprintf("%.4f", float64(v)/float64(aggregationNum)) + } + + g, err := gauge.New( + gauge.Height(1), + gauge.Border(linestyle.Light), + gauge.Color(cell.ColorMagenta), + gauge.BorderTitle("["+strValue+"]"), + gauge.HideTextProgress(), + gauge.TextLabel(item.Name), + ) + if err != nil { + return nil, err + } + + if err := g.Absolute(v, maxValue); err != nil { + return nil, err + } + ret.gauges = append(ret.gauges, g) + } + + return &ret, nil +} + +func layout(columns ...*metricColumn) ([]container.Option, error) { + var metricColumns []grid.Element + var columnWidthPerc int + + // For the best display effect, the maximum number of columns that can be displayed + const MaxColumnNum = 4 + // For the best display effect, the maximum number of gauges + // that can be displayed in each column + const MaxGaugeNum = 6 + const TitleHeight = 10 + + // Number of columns to display, each column represents a global metric + // The number should be less than or equal to MaxColumnNum + columnNum := int(math.Min(MaxColumnNum, float64(len(columns)))) + + // columnWidthPerc should be in the range (0, 100) + if columnNum > 1 { + columnWidthPerc = 100 / columnNum + } else { + columnWidthPerc = 99 + } + + for i := 0; i < columnNum; i++ { + var column []grid.Element + column = append(column, grid.RowHeightPerc(TitleHeight, grid.Widget(columns[i].title))) + + // Number of gauge in a column, each gauge represents a service or endpoint + // The number should be less than or equal to MaxGaugeNum + gaugeNum := int(math.Min(MaxGaugeNum, float64(len(columns[i].gauges)))) + gaugeHeight := int(math.Floor(float64(100-TitleHeight) / float64(gaugeNum))) + + for j := 0; j < gaugeNum; j++ { + column = append(column, grid.RowHeightPerc(gaugeHeight, grid.Widget(columns[i].gauges[j]))) + } + metricColumns = append(metricColumns, grid.ColWidthPerc(columnWidthPerc, column...)) + } + + builder := grid.New() + builder.Add( + grid.RowHeightPerc(10), + grid.RowHeightPerc(80, metricColumns...), + ) + + gridOpts, err := builder.Build() + if err != nil { + return nil, err + } + return gridOpts, nil +} + +func Display(ctx *cli.Context, metrics [][]*schema.SelectedRecord) error { + t, err := termbox.New() + if err != nil { + return err + } + defer t.Close() + + c, err := container.New( + t, + container.ID(RootID), + ) + if err != nil { + return err + } + + var columns []*metricColumn + + configs, err := dashboard.LoadTemplate(ctx.String("template")) + if err != nil { + return nil + } + + for i, config := range configs.Metrics { + col, innerErr := newMetricColumn(metrics[i], &config) + if innerErr != nil { + return innerErr + } + columns = append(columns, col) + } + + gridOpts, err := layout(columns...) + if err != nil { + return err + } + + err = c.Update(RootID, append( + gridOpts, + container.Border(linestyle.Light), + container.BorderTitle("[Global Metrics]-PRESS Q TO QUIT"))..., + ) + + if err != nil { + return err + } + + con, cancel := context.WithCancel(context.Background()) + quitter := func(keyboard *terminalapi.Keyboard) { + if strings.EqualFold(keyboard.Key.String(), "q") { + cancel() + } + } + + err = termdash.Run(con, t, c, termdash.KeyboardSubscriber(quitter)) + + return err +} diff --git a/display/graph/graph.go b/display/graph/graph.go index f951b51..c683c27 100644 --- a/display/graph/graph.go +++ b/display/graph/graph.go @@ -21,6 +21,10 @@ import ( "fmt" "reflect" + "github.com/urfave/cli" + + "github.com/apache/skywalking-cli/display/graph/gauge" + "github.com/apache/skywalking-cli/display/graph/tree" "github.com/apache/skywalking-cli/display/graph/heatmap" @@ -35,6 +39,7 @@ type ( LinearMetrics = map[string]float64 MultiLinearMetrics = []LinearMetrics Trace = schema.Trace + GlobalMetrics = [][]*schema.SelectedRecord ) var ( @@ -42,9 +47,10 @@ var ( LinearMetricsType = reflect.TypeOf(LinearMetrics{}) MultiLinearMetricsType = reflect.TypeOf(MultiLinearMetrics{}) TraceType = reflect.TypeOf(Trace{}) + GlobalMetricsType = reflect.TypeOf(GlobalMetrics{}) ) -func Display(displayable *d.Displayable) error { +func Display(ctx *cli.Context, displayable *d.Displayable) error { data := displayable.Data switch reflect.TypeOf(data) { @@ -60,6 +66,9 @@ func Display(displayable *d.Displayable) error { case TraceType: return tree.Display(tree.Adapt(data.(Trace))) + case GlobalMetricsType: + return gauge.Display(ctx, data.(GlobalMetrics)) + default: return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(data)) } diff --git a/example/Dashboard.Global.json b/example/Dashboard.Global.json new file mode 100644 index 0000000..74e55ba --- /dev/null +++ b/example/Dashboard.Global.json @@ -0,0 +1,69 @@ +{ + "metrics": [ + { + "condition": { + "name": "service_cpm", + "normal": true, + "scope": "Service", + "topN": 10, + "order": "DES" + }, + "title": " Service Load (calls/min) " + }, + { + "condition": { + "name": "service_resp_time", + "normal": true, + "scope": "Service", + "topN": 10, + "order": "DES" + }, + "title": " Slow Services (ms) " + }, + { + "condition": { + "name": "service_apdex", + "normal": true, + "scope": "Service", + "topN": 10, + "order": "ASC" + }, + "title": "Un-Health Services (Apdex)", + "aggregation": "/", + "aggregationNum": "10000" + }, + { + "condition": { + "name": "endpoint_avg", + "normal": true, + "scope": "Endpoint", + "topN": 10, + "order": "DES" + }, + "title": " Slow Endpoints (ms) " + } + ], + "responseLatency": { + "condition": { + "name": "all_percentile", + "entity": { + "scope": "All", + "normal": true + } + }, + "labels": "0, 1, 2, 3, 4", + "title": "Global Response Latency", + "unit": "percentile in ms" + }, + "heatMap": { + "condition": { + "name": "all_heatmap", + "entity": { + "scope": "All", + "normal": true + } + }, + "title": "Global Heatmap", + "unit": "ms" + } +} diff --git a/graphql/dashboard/global.go b/graphql/dashboard/global.go new file mode 100644 index 0000000..eb8cf13 --- /dev/null +++ b/graphql/dashboard/global.go @@ -0,0 +1,137 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package dashboard + +import ( + "encoding/json" + "io/ioutil" + "os" + + "github.com/machinebox/graphql" + "github.com/urfave/cli" + + "github.com/apache/skywalking-cli/assets" + "github.com/apache/skywalking-cli/graphql/client" + "github.com/apache/skywalking-cli/graphql/schema" +) + +type MetricTemplate struct { + Condition schema.TopNCondition `json:"condition"` + Title string `json:"title"` + Aggregation string `json:"aggregation"` + AggregationNum string `json:"aggregationNum"` +} + +type ChartTemplate struct { + Condition schema.MetricsCondition `json:"condition"` + Title string `json:"title"` + Unit string `json:"unit"` + Labels string `json:"labels"` +} + +type GlobalTemplate struct { + Metrics []MetricTemplate `json:"metrics"` + ResponseLatency ChartTemplate `json:"responseLatency"` + HeatMap ChartTemplate `json:"heatMap"` +} + +type GlobalData struct { + Metrics [][]*schema.SelectedRecord `json:"metrics"` + ResponseLatency []*schema.MetricsValues `json:"responseLatency"` + HeatMap schema.HeatMap `json:"heatMap"` +} + +const DefaultTemplatePath = "templates/Dashboard.Global.json" + +func LoadTemplate(filename string) (*GlobalTemplate, error) { + var config GlobalTemplate + var byteValue []byte + + if filename == DefaultTemplatePath { + jsonFile := assets.Read(filename) + byteValue = []byte(jsonFile) + } else { + jsonFile, err := os.Open(filename) + if err != nil { + return nil, err + } + defer jsonFile.Close() + + byteValue, err = ioutil.ReadAll(jsonFile) + if err != nil { + return nil, err + } + } + + if err := json.Unmarshal(byteValue, &config); err != nil { + return nil, err + } + return &config, nil +} + +func Metrics(ctx *cli.Context, duration schema.Duration) [][]*schema.SelectedRecord { + var ret [][]*schema.SelectedRecord + configs, err := LoadTemplate(ctx.String("template")) + if err != nil { + return nil + } + + for _, m := range configs.Metrics { + var response map[string][]*schema.SelectedRecord + request := graphql.NewRequest(assets.Read("graphqls/dashboard/SortMetrics.graphql")) + request.Var("condition", m.Condition) + request.Var("duration", duration) + + client.ExecuteQueryOrFail(ctx, request, &response) + ret = append(ret, response["result"]) + } + + return ret +} + +func responseLatency(ctx *cli.Context, duration schema.Duration) []*schema.MetricsValues { + var response map[string][]*schema.MetricsValues + + request := graphql.NewRequest(assets.Read("graphqls/dashboard/LabeledMetricsValues.graphql")) + request.Var("duration", duration) + + client.ExecuteQueryOrFail(ctx, request, &response) + + return response["result"] +} + +func heatMap(ctx *cli.Context, duration schema.Duration) schema.HeatMap { + var response map[string]schema.HeatMap + + request := graphql.NewRequest(assets.Read("graphqls/dashboard/HeatMap.graphql")) + request.Var("duration", duration) + + client.ExecuteQueryOrFail(ctx, request, &response) + + return response["result"] +} + +func Global(ctx *cli.Context, duration schema.Duration) *GlobalData { + var globalData GlobalData + + globalData.Metrics = Metrics(ctx, duration) + globalData.ResponseLatency = responseLatency(ctx, duration) + globalData.HeatMap = heatMap(ctx, duration) + + return &globalData +}