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
+}

Reply via email to