You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2019/11/26 09:26:03 UTC
[skywalking-cli] branch master updated: [Feature] Add metrics
commands and support display in AsciiGraph style (#14)
This is an automated email from the ASF dual-hosted git repository.
wusheng 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 6e0db0a [Feature] Add metrics commands and support display in AsciiGraph style (#14)
6e0db0a is described below
commit 6e0db0aae85034eea1667456bb0ac2ff99b55454
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Tue Nov 26 17:25:56 2019 +0800
[Feature] Add metrics commands and support display in AsciiGraph style (#14)
* Add metrics commands and support display in AsciiGraph style
* Refactor to avoid hard-coded metrics name
* Polish and lint codes
* Add documentation
* Rename metrics command to linear-metrics
* Polish documentation
* Rename
---
README.md | 10 ++
display/display.go => commands/flags/flags.go | 38 ++-----
commands/interceptor/duration.go | 26 +----
commands/metrics/linear-metrics.go | 63 ++++++++++++
display/display.go | 5 +
display/{display.go => graph/graph.go} | 34 ++-----
display/graph/linear/linear.go | 118 ++++++++++++++++++++++
go.mod | 2 +
go.sum | 4 +
graphql/client/client.go | 35 +++++++
display/display.go => graphql/schema/constants.go | 47 ++++-----
swctl/main.go | 3 +
12 files changed, 280 insertions(+), 105 deletions(-)
diff --git a/README.md b/README.md
index a6529b4..f144b79 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,16 @@ and it also has some options and third-level commands.
| `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
| `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
+### `linear-metrics` second-level command
+`linear-metrics` second-level command is an entrance for all operations related to linear metrics,
+and it also has some options.
+
+| option | description | default |
+| : --- | : --- | : --- |
+| `--name` | Metrics name, defined in [OAL](https://github.com/apache/skywalking/blob/master/oap-server/server-bootstrap/src/main/resources/official_analysis.oal), such as `all_p99`, etc. |
+| `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
+| `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
+
# Developer guide
## Compiling and building
diff --git a/display/display.go b/commands/flags/flags.go
similarity index 53%
copy from display/display.go
copy to commands/flags/flags.go
index 63355db..ba41e27 100644
--- a/display/display.go
+++ b/commands/flags/flags.go
@@ -15,37 +15,17 @@
// specific language governing permissions and limitations
// under the License.
-package display
+package flags
-import (
- "fmt"
- "strings"
+import "github.com/urfave/cli"
- "github.com/urfave/cli"
+// Flags concatenates the `flags` into one []cli.Flag
+func Flags(flags ...[]cli.Flag) []cli.Flag {
+ var result []cli.Flag
- "github.com/apache/skywalking-cli/display/json"
- "github.com/apache/skywalking-cli/display/table"
- "github.com/apache/skywalking-cli/display/yaml"
-)
-
-const (
- JSON string = "json"
- YAML string = "yaml"
- TABLE string = "table"
-)
-
-// Display the object in the style specified in flag --display
-func Display(ctx *cli.Context, object interface{}) error {
- displayStyle := ctx.GlobalString("display")
-
- switch strings.ToLower(displayStyle) {
- case JSON:
- return json.Display(object)
- case YAML:
- return yaml.Display(object)
- case TABLE:
- return table.Display(object)
- default:
- return fmt.Errorf("unsupported display style: %s", displayStyle)
+ for _, flags := range flags {
+ result = append(result, flags...)
}
+
+ return result
}
diff --git a/commands/interceptor/duration.go b/commands/interceptor/duration.go
index 820d6e7..f17c2d9 100644
--- a/commands/interceptor/duration.go
+++ b/commands/interceptor/duration.go
@@ -26,25 +26,9 @@ import (
"github.com/apache/skywalking-cli/logger"
)
-var stepFormats = map[schema.Step]string{
- schema.StepSecond: "2006-01-02 150400",
- schema.StepMinute: "2006-01-02 1504",
- schema.StepHour: "2006-01-02 15",
- schema.StepDay: "2006-01-02",
- schema.StepMonth: "2006-01",
-}
-
-var stepDuration = map[schema.Step]time.Duration{
- schema.StepSecond: time.Second,
- schema.StepMinute: time.Minute,
- schema.StepHour: time.Hour,
- schema.StepDay: time.Hour * 24,
- schema.StepMonth: time.Hour * 24 * 30,
-}
-
func tryParseTime(unparsed string) (schema.Step, time.Time, error) {
var possibleError error = nil
- for step, layout := range stepFormats {
+ for step, layout := range schema.StepFormats {
t, err := time.Parse(layout, unparsed)
if err == nil {
return step, t, nil
@@ -62,9 +46,9 @@ func DurationInterceptor(ctx *cli.Context) error {
startTime, endTime, step := ParseDuration(start, end)
- if err := ctx.Set("start", startTime.Format(stepFormats[step])); err != nil {
+ if err := ctx.Set("start", startTime.Format(schema.StepFormats[step])); err != nil {
return err
- } else if err := ctx.Set("end", endTime.Format(stepFormats[step])); err != nil {
+ } else if err := ctx.Set("end", endTime.Format(schema.StepFormats[step])); err != nil {
return err
} else if err := ctx.Set("step", step.String()); err != nil {
return err
@@ -110,12 +94,12 @@ func ParseDuration(start, end string) (startTime, endTime time.Time, step schema
if step, startTime, err = tryParseTime(start); err != nil {
logger.Log.Fatalln("Unsupported time format:", start, err)
}
- return startTime, startTime.Add(30 * stepDuration[step]), step
+ return startTime, startTime.Add(30 * schema.StepDuration[step]), step
} else { // start is absent
if step, endTime, err = tryParseTime(end); err != nil {
logger.Log.Fatalln("Unsupported time format:", end, err)
}
- return endTime.Add(-30 * stepDuration[step]), endTime, step
+ return endTime.Add(-30 * schema.StepDuration[step]), endTime, step
}
}
diff --git a/commands/metrics/linear-metrics.go b/commands/metrics/linear-metrics.go
new file mode 100644
index 0000000..e3148fe
--- /dev/null
+++ b/commands/metrics/linear-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 metrics
+
+import (
+ "github.com/urfave/cli"
+
+ "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/graphql/client"
+ "github.com/apache/skywalking-cli/graphql/schema"
+)
+
+var Command = cli.Command{
+ Name: "linear-metrics",
+ Usage: "Query linear metrics defined in backend OAL",
+ Flags: flags.Flags(
+ flags.DurationFlags,
+ []cli.Flag{
+ cli.StringFlag{
+ Name: "name",
+ Usage: "metrics `NAME`, such as `all_p99`",
+ Required: true,
+ },
+ },
+ ),
+ Before: interceptor.BeforeChain([]cli.BeforeFunc{
+ interceptor.DurationInterceptor,
+ }),
+ Action: func(ctx *cli.Context) error {
+ end := ctx.String("end")
+ start := ctx.String("start")
+ step := ctx.Generic("step")
+ metricsName := ctx.String("name")
+
+ metricsValues := client.LinearIntValues(ctx, schema.MetricCondition{
+ Name: metricsName,
+ }, schema.Duration{
+ Start: start,
+ End: end,
+ Step: step.(*model.StepEnumValue).Selected,
+ })
+
+ return display.Display(ctx, metricsValues)
+ },
+}
diff --git a/display/display.go b/display/display.go
index 63355db..48be6ab 100644
--- a/display/display.go
+++ b/display/display.go
@@ -21,6 +21,8 @@ import (
"fmt"
"strings"
+ "github.com/apache/skywalking-cli/display/graph"
+
"github.com/urfave/cli"
"github.com/apache/skywalking-cli/display/json"
@@ -32,6 +34,7 @@ const (
JSON string = "json"
YAML string = "yaml"
TABLE string = "table"
+ GRAPH string = "graph"
)
// Display the object in the style specified in flag --display
@@ -45,6 +48,8 @@ func Display(ctx *cli.Context, object interface{}) error {
return yaml.Display(object)
case TABLE:
return table.Display(object)
+ case GRAPH:
+ return graph.Display(object)
default:
return fmt.Errorf("unsupported display style: %s", displayStyle)
}
diff --git a/display/display.go b/display/graph/graph.go
similarity index 55%
copy from display/display.go
copy to display/graph/graph.go
index 63355db..7184360 100644
--- a/display/display.go
+++ b/display/graph/graph.go
@@ -15,37 +15,21 @@
// specific language governing permissions and limitations
// under the License.
-package display
+package graph
import (
"fmt"
- "strings"
+ "reflect"
- "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/display/graph/linear"
)
-const (
- JSON string = "json"
- YAML string = "yaml"
- TABLE string = "table"
-)
+func Display(object interface{}) error {
+ if reflect.TypeOf(object) != reflect.TypeOf(map[string]float64{}) {
+ return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(object))
+ }
-// Display the object in the style specified in flag --display
-func Display(ctx *cli.Context, object interface{}) error {
- displayStyle := ctx.GlobalString("display")
+ kvs := object.(map[string]float64)
- switch strings.ToLower(displayStyle) {
- case JSON:
- return json.Display(object)
- case YAML:
- return yaml.Display(object)
- case TABLE:
- return table.Display(object)
- default:
- return fmt.Errorf("unsupported display style: %s", displayStyle)
- }
+ return linear.Display(kvs)
}
diff --git a/display/graph/linear/linear.go b/display/graph/linear/linear.go
new file mode 100644
index 0000000..b6e6d07
--- /dev/null
+++ b/display/graph/linear/linear.go
@@ -0,0 +1,118 @@
+// 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 linear
+
+import (
+ "context"
+ "strings"
+
+ "github.com/mum4k/termdash/widgetapi"
+
+ "github.com/mum4k/termdash"
+ "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/linechart"
+)
+
+const RootID = "root"
+
+func newWidgets(inputs map[string]float64) (lineChart *linechart.LineChart, err error) {
+ index := 0
+
+ xLabels := map[int]string{}
+ var yValues []float64
+ for xLabel, yValue := range inputs {
+ xLabels[index] = xLabel
+ index++
+ yValues = append(yValues, yValue)
+ }
+
+ if lineChart, err = linechart.New(
+ linechart.YAxisAdaptive(),
+ ); err != nil {
+ return
+ }
+
+ err = lineChart.Series("graph-linear", yValues, linechart.SeriesXLabels(xLabels))
+
+ return lineChart, err
+}
+
+func gridLayout(lineChart widgetapi.Widget) ([]container.Option, error) {
+ widget := grid.Widget(
+ lineChart,
+ container.Border(linestyle.Light),
+ container.BorderTitleAlignCenter(),
+ container.BorderTitle("Press q to quit"),
+ )
+
+ builder := grid.New()
+ builder.Add(widget)
+
+ return builder.Build()
+}
+
+func Display(inputs map[string]float64) error {
+ t, err := termbox.New()
+ if err != nil {
+ return err
+ }
+ defer t.Close()
+
+ c, err := container.New(
+ t,
+ container.ID(RootID),
+ container.PaddingTop(2),
+ container.PaddingRight(2),
+ container.PaddingBottom(2),
+ container.PaddingLeft(2),
+ )
+ if err != nil {
+ return err
+ }
+
+ w, err := newWidgets(inputs)
+ if err != nil {
+ return err
+ }
+
+ gridOpts, err := gridLayout(w)
+ if err != nil {
+ return err
+ }
+
+ err = c.Update(RootID, gridOpts...)
+
+ if err != nil {
+ return err
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ quitter := func(keyboard *terminalapi.Keyboard) {
+ if strings.EqualFold(keyboard.Key.String(), "q") {
+ cancel()
+ }
+ }
+
+ err = termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter))
+
+ return err
+}
diff --git a/go.mod b/go.mod
index c859fb1..9ab591f 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,8 @@ go 1.13
require (
github.com/machinebox/graphql v0.2.2
+ github.com/mum4k/termdash v0.10.0
+ github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 // indirect
github.com/olekukonko/tablewriter v0.0.2
github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.4.2
diff --git a/go.sum b/go.sum
index 0b55309..0203faf 100644
--- a/go.sum
+++ b/go.sum
@@ -10,6 +10,10 @@ github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkB
github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mum4k/termdash v0.10.0 h1:uqM6ePiMf+smecb1tJJeON36o1hREeCfOmLFG0iz4a0=
+github.com/mum4k/termdash v0.10.0/go.mod h1:l3tO+lJi9LZqXRq7cu7h5/8rDIK3AzelSuq2v/KncxI=
+github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 h1:hhGN4SFXgXo61Q4Sjj/X9sBjyeSa2kdpaOzCO+8EVQw=
+github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2 h1:sq53g+DWf0J6/ceFUHpQ0nAEb6WgM++fq16MZ91cS6o=
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
diff --git a/graphql/client/client.go b/graphql/client/client.go
index fb870a0..1f87a55 100644
--- a/graphql/client/client.go
+++ b/graphql/client/client.go
@@ -20,6 +20,7 @@ package client
import (
"context"
"fmt"
+ "time"
"github.com/machinebox/graphql"
"github.com/urfave/cli"
@@ -100,3 +101,37 @@ func SearchService(cliCtx *cli.Context, serviceCode string) (service schema.Serv
}
return service, nil
}
+
+func LinearIntValues(ctx *cli.Context, condition schema.MetricCondition, duration schema.Duration) map[string]float64 {
+ var response map[string]schema.IntValues
+
+ request := graphql.NewRequest(`
+ query ($metric: MetricCondition!, $duration: Duration!) {
+ metrics: getLinearIntValues(metric: $metric, duration: $duration) {
+ values { value }
+ }
+ }
+ `)
+ request.Var("metric", condition)
+ request.Var("duration", duration)
+
+ executeQuery(ctx, request, &response)
+
+ values := metricsToMap(duration, response["metrics"].Values)
+
+ return values
+}
+
+func metricsToMap(duration schema.Duration, kvInts []*schema.KVInt) map[string]float64 {
+ values := map[string]float64{}
+ format := schema.StepFormats[duration.Step]
+ startTime, err := time.Parse(format, duration.Start)
+ if err != nil {
+ logger.Log.Fatalln(err)
+ }
+ step := schema.StepDuration[duration.Step]
+ for idx, value := range kvInts {
+ values[startTime.Add(time.Duration(idx)*step).Format(format)] = float64(value.Value)
+ }
+ return values
+}
diff --git a/display/display.go b/graphql/schema/constants.go
similarity index 53%
copy from display/display.go
copy to graphql/schema/constants.go
index 63355db..f146338 100644
--- a/display/display.go
+++ b/graphql/schema/constants.go
@@ -15,37 +15,24 @@
// specific language governing permissions and limitations
// under the License.
-package display
+package schema
-import (
- "fmt"
- "strings"
+import "time"
- "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"
-)
-
-const (
- JSON string = "json"
- YAML string = "yaml"
- TABLE string = "table"
-)
-
-// Display the object in the style specified in flag --display
-func Display(ctx *cli.Context, object interface{}) error {
- displayStyle := ctx.GlobalString("display")
+// StepFormats is a mapping from schema.Step to its time format
+var StepFormats = map[Step]string{
+ StepSecond: "2006-01-02 150400",
+ StepMinute: "2006-01-02 1504",
+ StepHour: "2006-01-02 15",
+ StepDay: "2006-01-02",
+ StepMonth: "2006-01",
+}
- switch strings.ToLower(displayStyle) {
- case JSON:
- return json.Display(object)
- case YAML:
- return yaml.Display(object)
- case TABLE:
- return table.Display(object)
- default:
- return fmt.Errorf("unsupported display style: %s", displayStyle)
- }
+// StepDuration is a mapping from schema.Step to its time.Duration
+var StepDuration = map[Step]time.Duration{
+ StepSecond: time.Second,
+ StepMinute: time.Minute,
+ StepHour: time.Hour,
+ StepDay: time.Hour * 24,
+ StepMonth: time.Hour * 24 * 30,
}
diff --git a/swctl/main.go b/swctl/main.go
index 3aa564a..dfc5248 100644
--- a/swctl/main.go
+++ b/swctl/main.go
@@ -21,6 +21,8 @@ import (
"io/ioutil"
"os"
+ "github.com/apache/skywalking-cli/commands/metrics"
+
"github.com/apache/skywalking-cli/commands/instance"
"github.com/sirupsen/logrus"
@@ -71,6 +73,7 @@ func main() {
app.Commands = []cli.Command{
service.Command,
instance.Command,
+ metrics.Command,
}
app.Before = interceptor.BeforeChain([]cli.BeforeFunc{