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 2020/03/06 12:01:15 UTC

[skywalking-cli] branch feature/linear-metrics updated (e697a78 -> 81d133b)

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

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


 discard e697a78  [Feature] Support multiple linear metrics and enhance Ascii Graph Display
     new 81d133b  [Feature] Support multiple linear metrics and enhance Ascii Graph Display

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (e697a78)
            \
             N -- N -- N   refs/heads/feature/linear-metrics (81d133b)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

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.


Summary of changes:
 Makefile                                           |  1 +
 README.md                                          | 34 ++++++++++++----
 commands/metrics/linear/linear-metrics.go          | 45 ++++------------------
 ...inear-metrics.go => multiple-linear-metrics.go} | 28 +++-----------
 .../{service/service.go => metrics/metrics.go}     | 14 ++++---
 commands/metrics/single/single-metrics.go          |  2 +-
 graphql/metrics/metrics.go                         | 18 +++++++++
 graphql/utils/{strings.go => adapter.go}           | 18 +++++++--
 swctl/main.go                                      | 12 +++---
 9 files changed, 86 insertions(+), 86 deletions(-)
 copy commands/metrics/linear/{linear-metrics.go => multiple-linear-metrics.go} (79%)
 copy commands/{service/service.go => metrics/metrics.go} (78%)
 rename graphql/utils/{strings.go => adapter.go} (70%)


[skywalking-cli] 01/01: [Feature] Support multiple linear metrics and enhance Ascii Graph Display

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch feature/linear-metrics
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git

commit 81d133b673eac2185f5e6343aed4d21e76017643
Author: kezhenxu94 <ke...@163.com>
AuthorDate: Fri Mar 6 18:00:53 2020 +0800

    [Feature] Support multiple linear metrics and enhance Ascii Graph Display
    
    ### Motivation
    
    Adopt the new API of multiple linear metrics, https://github.com/apache/skywalking/pull/4214
    
    ### Modification
    
    - Rewrite the pxx metrics with the new API
    
    - Slightly refactor the GraphQL client into individual package, according to the query protocol
    
    - Enhance the Ascii Graph display style
    
    ### Result
    
    - CLI is compatible with the latest backend
    
    - Ascii Graph can display multiple charts in one screen
---
 .golangci.yml                                      |   2 +-
 Makefile                                           |   1 +
 README.md                                          |  34 +++--
 commands/endpoint/list.go                          |   5 +-
 commands/instance/instance.go                      |   5 +-
 commands/instance/list.go                          |   5 +-
 commands/instance/search.go                        |   5 +-
 commands/interceptor/duration.go                   |  12 +-
 commands/metrics/linear/linear-metrics.go          |  22 +--
 ...inear-metrics.go => multiple-linear-metrics.go} |  39 ++++--
 .../graph/graph.go => commands/metrics/metrics.go  |  24 ++--
 commands/metrics/single/single-metrics.go          |   9 +-
 commands/service/list.go                           |   7 +-
 display/display.go                                 |   8 +-
 display/graph/graph.go                             |  14 +-
 display/graph/linear/linear.go                     |  82 +++++++----
 graphql/client/client.go                           | 150 +--------------------
 graphql/metadata/metadata.go                       |  93 ++++++++++++-
 graphql/metrics/metrics.go                         |  82 +++++++++++
 graphql/{metadata/metadata.go => utils/adapter.go} |  37 ++---
 graphql/{schema => utils}/constants.go             |  32 +++--
 swctl/main.go                                      |  12 +-
 22 files changed, 399 insertions(+), 281 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index cebf66b..26b94e2 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -38,7 +38,7 @@ linters-settings:
   misspell:
     locale: US
   lll:
-    line-length: 120
+    line-length: 150
   goimports:
     local-prefixes: github.com/apache/skywalking-cli
   gocritic:
diff --git a/Makefile b/Makefile
index 8ddb4eb..cae27cf 100644
--- a/Makefile
+++ b/Makefile
@@ -94,6 +94,7 @@ clean:
 	-rm -rf *.tgz
 	-rm -rf *.asc
 	-rm -rf *.sha512
+	-rm -rf query-protocol/schema.graphqls
 
 release-src: clean
 	-tar -zcvf $(RELEASE_SRC).tgz \
diff --git a/README.md b/README.md
index 00f225c..6040715 100644
--- a/README.md
+++ b/README.md
@@ -149,11 +149,28 @@ This section covers all the available commands in SkyWalking CLI and their usage
 
 </details>
 
-### `linear-metrics`
+### `metrics`
+
+#### `metrics linear`
+
+<details>
+
+<summary>metrics linear [--start=start-time] [--end=end-time] --name=metrics-name [--id=entity-id]</summary>
+
+| 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. |
+| `--id` | the related id if the metrics requires one, e.g. for metrics `service_p99`, the service `id` is required, use `--id` to specify the service id, the same for `instance`, `endpoint`, etc. |
+| `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
+| `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
+
+</details>
+
+#### `metrics multiple-linear`
 
 <details>
 
-<summary>linear-metrics [--start=start-time] [--end=end-time] --name=metrics-name [--id=entity-id]</summary>
+<summary>metrics multiple-linear [--start=start-time] [--end=end-time] --name=metrics-name [--id=entity-id] [--num=number-of-linear-metrics]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -161,14 +178,15 @@ This section covers all the available commands in SkyWalking CLI and their usage
 | `--id` | the related id if the metrics requires one, e.g. for metrics `service_p99`, the service `id` is required, use `--id` to specify the service id, the same for `instance`, `endpoint`, etc. |
 | `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
+| `--num` | Number of the linear metrics to fetch | `5` |
 
 </details>
 
-### `single-metrics`
+#### `metrics single`
 
 <details>
 
-<summary>single-metrics [--start=start-time] [--end=end-time] --name=metrics-name [--ids=entity-ids]</summary>
+<summary>metrics single [--start=start-time] [--end=end-time] --name=metrics-name [--ids=entity-ids]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -239,7 +257,7 @@ otherwise,
 If you have already got the `id` of the instance:
 
 ```shell
-$ ./bin/swctl --display=graph linear-metrics --name=service_instance_resp_time --id 5
+$ ./bin/swctl --display=graph metrics linear --name=service_instance_resp_time --id 5
 ┌─────────────────────────────────────────────────────────────────────────────────Press q to quit──────────────────────────────────────────────────────────────────────────────────┐
 │                                                                                                                                                                                  │
 │                                                                                                                                                                                  │
@@ -263,7 +281,7 @@ $ ./bin/swctl --display=graph linear-metrics --name=service_instance_resp_time -
 otherwise
 
 ```shell
-$ ./bin/swctl instance ls --service-name=projectC | jq '.[] | select(.name == "projectC-pid:7895@skywalking-server-0001").id' | xargs ./bin/swctl --display=graph linear-metrics --name=service_instance_resp_time --id
+$ ./bin/swctl instance ls --service-name=projectC | jq '.[] | select(.name == "projectC-pid:7895@skywalking-server-0001").id' | xargs ./bin/swctl --display=graph metrics linear --name=service_instance_resp_time --id
 ┌─────────────────────────────────────────────────────────────────────────────────Press q to quit──────────────────────────────────────────────────────────────────────────────────┐
 │                                                                                                                                                                                  │
 │                                                                                                                                                                                  │
@@ -291,7 +309,7 @@ $ ./bin/swctl instance ls --service-name=projectC | jq '.[] | select(.name == "p
 <summary>Query a single metrics value for a specific endpoint</summary>
 
 ```shell
-$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl single-metrics --name endpoint_cpm --ids
+$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl metrics single --name endpoint_cpm --ids
 [{"id":"22","value":116}]
 ```
 
@@ -302,7 +320,7 @@ $ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint ls
 <summary>Query metrics single values for all endpoints of service of id 3</summary>
 
 ```shell
-$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl single-metrics --name endpoint_cpm --end='2019-12-02 2137' --ids
+$ ./bin/swctl service ls projectC | jq '.[0].id' | xargs ./bin/swctl endpoint ls --service-id | jq '.[] | [.id] | join(",")' | xargs ./bin/swctl metrics single --name endpoint_cpm --end='2019-12-02 2137' --ids
 [{"id":"3","value":116}]
 ```
 
diff --git a/commands/endpoint/list.go b/commands/endpoint/list.go
index 51ea320..034c6a5 100644
--- a/commands/endpoint/list.go
+++ b/commands/endpoint/list.go
@@ -20,8 +20,9 @@ package endpoint
 import (
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/graphql/metadata"
+
 	"github.com/apache/skywalking-cli/display"
-	"github.com/apache/skywalking-cli/graphql/client"
 )
 
 var ListCommand = cli.Command{
@@ -53,7 +54,7 @@ var ListCommand = cli.Command{
 		limit := ctx.Int("limit")
 		keyword := ctx.String("keyword")
 
-		endpoints := client.SearchEndpoints(ctx, serviceID, keyword, limit)
+		endpoints := metadata.SearchEndpoints(ctx, serviceID, keyword, limit)
 
 		return display.Display(ctx, endpoints)
 	},
diff --git a/commands/instance/instance.go b/commands/instance/instance.go
index da83c80..29c86a7 100644
--- a/commands/instance/instance.go
+++ b/commands/instance/instance.go
@@ -20,7 +20,8 @@ package instance
 import (
 	"github.com/urfave/cli"
 
-	"github.com/apache/skywalking-cli/graphql/client"
+	"github.com/apache/skywalking-cli/graphql/metadata"
+
 	"github.com/apache/skywalking-cli/logger"
 )
 
@@ -43,7 +44,7 @@ func verifyAndSwitchServiceParameter(ctx *cli.Context) string {
 	}
 
 	if serviceID == "" && serviceName != "" {
-		service, err := client.SearchService(ctx, serviceName)
+		service, err := metadata.SearchService(ctx, serviceName)
 		if err != nil {
 			logger.Log.Fatalln(err)
 		}
diff --git a/commands/instance/list.go b/commands/instance/list.go
index c20ccb4..eda72c1 100644
--- a/commands/instance/list.go
+++ b/commands/instance/list.go
@@ -20,11 +20,12 @@ package instance
 import (
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/graphql/metadata"
+
 	"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"
 )
 
@@ -44,7 +45,7 @@ var ListCommand = cli.Command{
 		start := ctx.String("start")
 		step := ctx.Generic("step")
 
-		instances := client.Instances(ctx, serviceID, schema.Duration{
+		instances := metadata.Instances(ctx, serviceID, schema.Duration{
 			Start: start,
 			End:   end,
 			Step:  step.(*model.StepEnumValue).Selected,
diff --git a/commands/instance/search.go b/commands/instance/search.go
index b9e2ac9..b97adae 100644
--- a/commands/instance/search.go
+++ b/commands/instance/search.go
@@ -20,13 +20,14 @@ package instance
 import (
 	"regexp"
 
+	"github.com/apache/skywalking-cli/graphql/metadata"
+
 	"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"
 )
 
@@ -47,7 +48,7 @@ var SearchCommand = cli.Command{
 
 		regex := ctx.String("regex")
 
-		instances := client.Instances(ctx, serviceID, schema.Duration{
+		instances := metadata.Instances(ctx, serviceID, schema.Duration{
 			Start: start,
 			End:   end,
 			Step:  step.(*model.StepEnumValue).Selected,
diff --git a/commands/interceptor/duration.go b/commands/interceptor/duration.go
index 3eb355a..c52ae69 100644
--- a/commands/interceptor/duration.go
+++ b/commands/interceptor/duration.go
@@ -21,6 +21,8 @@ import (
 	"strconv"
 	"time"
 
+	"github.com/apache/skywalking-cli/graphql/utils"
+
 	"github.com/urfave/cli"
 
 	"github.com/apache/skywalking-cli/graphql/schema"
@@ -29,7 +31,7 @@ import (
 
 func tryParseTime(unparsed string) (schema.Step, time.Time, error) {
 	var possibleError error = nil
-	for step, layout := range schema.StepFormats {
+	for step, layout := range utils.StepFormats {
 		t, err := time.Parse(layout, unparsed)
 		if err == nil {
 			return step, t, nil
@@ -48,9 +50,9 @@ func DurationInterceptor(ctx *cli.Context) error {
 
 	startTime, endTime, step := ParseDuration(start, end, timezone)
 
-	if err := ctx.Set("start", startTime.Format(schema.StepFormats[step])); err != nil {
+	if err := ctx.Set("start", startTime.Format(utils.StepFormats[step])); err != nil {
 		return err
-	} else if err := ctx.Set("end", endTime.Format(schema.StepFormats[step])); err != nil {
+	} else if err := ctx.Set("end", endTime.Format(utils.StepFormats[step])); err != nil {
 		return err
 	} else if err := ctx.Set("step", step.String()); err != nil {
 		return err
@@ -103,12 +105,12 @@ func ParseDuration(start, end, timezone string) (startTime, endTime time.Time, s
 		if step, startTime, err = tryParseTime(start); err != nil {
 			logger.Log.Fatalln("Unsupported time format:", start, err)
 		}
-		return startTime, startTime.Add(30 * schema.StepDuration[step]), step
+		return startTime, startTime.Add(30 * utils.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 * schema.StepDuration[step]), endTime, step
+		return endTime.Add(-30 * utils.StepDuration[step]), endTime, step
 	}
 }
 
diff --git a/commands/metrics/linear/linear-metrics.go b/commands/metrics/linear/linear-metrics.go
index ceda447..d435063 100644
--- a/commands/metrics/linear/linear-metrics.go
+++ b/commands/metrics/linear/linear-metrics.go
@@ -20,16 +20,18 @@ package linear
 import (
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/graphql/metrics"
+	"github.com/apache/skywalking-cli/graphql/utils"
+
 	"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",
+var Single = cli.Command{
+	Name:  "linear",
 	Usage: "Query linear metrics defined in backend OAL",
 	Flags: flags.Flags(
 		flags.DurationFlags,
@@ -62,15 +64,17 @@ var Command = cli.Command{
 			id = &idString
 		}
 
-		metricsValues := client.LinearIntValues(ctx, schema.MetricCondition{
-			Name: metricsName,
-			ID:   id,
-		}, schema.Duration{
+		duration := schema.Duration{
 			Start: start,
 			End:   end,
 			Step:  step.(*model.StepEnumValue).Selected,
-		})
+		}
+
+		metricsValues := metrics.LinearIntValues(ctx, schema.MetricCondition{
+			Name: metricsName,
+			ID:   id,
+		}, duration)
 
-		return display.Display(ctx, metricsValues)
+		return display.Display(ctx, utils.MetricsToMap(duration, metricsValues))
 	},
 }
diff --git a/commands/metrics/linear/linear-metrics.go b/commands/metrics/linear/multiple-linear-metrics.go
similarity index 70%
copy from commands/metrics/linear/linear-metrics.go
copy to commands/metrics/linear/multiple-linear-metrics.go
index ceda447..31db251 100644
--- a/commands/metrics/linear/linear-metrics.go
+++ b/commands/metrics/linear/multiple-linear-metrics.go
@@ -20,23 +20,25 @@ package linear
 import (
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/graphql/metrics"
+	"github.com/apache/skywalking-cli/graphql/utils"
+
 	"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",
+var Multiple = cli.Command{
+	Name:  "multiple-linear",
+	Usage: "Query multiple linear metrics defined in backend OAL",
 	Flags: flags.Flags(
 		flags.DurationFlags,
 		[]cli.Flag{
 			cli.StringFlag{
 				Name:     "name",
-				Usage:    "metrics `NAME`, such as `all_p99`",
+				Usage:    "metrics `NAME`, such as `all_percentile`",
 				Required: true,
 			},
 			cli.StringFlag{
@@ -44,6 +46,12 @@ var Command = cli.Command{
 				Usage:    "`ID`, the related id if the metrics requires one",
 				Required: false,
 			},
+			cli.IntFlag{
+				Name:     "num",
+				Usage:    "`num`, the number of linear metrics to query, (default: 5)",
+				Required: false,
+				Value:    5,
+			},
 		},
 	),
 	Before: interceptor.BeforeChain([]cli.BeforeFunc{
@@ -55,6 +63,7 @@ var Command = cli.Command{
 		start := ctx.String("start")
 		step := ctx.Generic("step")
 		metricsName := ctx.String("name")
+		numOfLinear := ctx.Int("num")
 
 		var id *string = nil
 
@@ -62,15 +71,23 @@ var Command = cli.Command{
 			id = &idString
 		}
 
-		metricsValues := client.LinearIntValues(ctx, schema.MetricCondition{
-			Name: metricsName,
-			ID:   id,
-		}, schema.Duration{
+		duration := schema.Duration{
 			Start: start,
 			End:   end,
 			Step:  step.(*model.StepEnumValue).Selected,
-		})
+		}
+
+		values := metrics.MultipleLinearIntValues(ctx, schema.MetricCondition{
+			Name: metricsName,
+			ID:   id,
+		}, numOfLinear, duration)
+
+		reshaped := make([]map[string]float64, len(values))
+
+		for index, value := range values {
+			reshaped[index] = utils.MetricsToMap(duration, value)
+		}
 
-		return display.Display(ctx, metricsValues)
+		return display.Display(ctx, reshaped)
 	},
 }
diff --git a/display/graph/graph.go b/commands/metrics/metrics.go
similarity index 69%
copy from display/graph/graph.go
copy to commands/metrics/metrics.go
index 7184360..8f941b9 100644
--- a/display/graph/graph.go
+++ b/commands/metrics/metrics.go
@@ -15,21 +15,21 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package graph
+package metrics
 
 import (
-	"fmt"
-	"reflect"
+	"github.com/urfave/cli"
 
-	"github.com/apache/skywalking-cli/display/graph/linear"
+	"github.com/apache/skywalking-cli/commands/metrics/linear"
+	"github.com/apache/skywalking-cli/commands/metrics/single"
 )
 
-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))
-	}
-
-	kvs := object.(map[string]float64)
-
-	return linear.Display(kvs)
+var Command = cli.Command{
+	Name:  "metrics",
+	Usage: "Query metrics defined in backend OAL",
+	Subcommands: cli.Commands{
+		single.Command,
+		linear.Single,
+		linear.Multiple,
+	},
 }
diff --git a/commands/metrics/single/single-metrics.go b/commands/metrics/single/single-metrics.go
index 99817da..69405ea 100644
--- a/commands/metrics/single/single-metrics.go
+++ b/commands/metrics/single/single-metrics.go
@@ -20,18 +20,19 @@ package single
 import (
 	"strings"
 
+	"github.com/apache/skywalking-cli/graphql/metrics"
+
 	"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:  "single-metrics",
+	Name:  "single",
 	Usage: "Query single metrics defined in backend OAL",
 	Flags: flags.Flags(
 		flags.DurationFlags,
@@ -65,7 +66,7 @@ var Command = cli.Command{
 			ids = append(ids, strings.Split(id, ",")...)
 		}
 
-		metricsValues := client.IntValues(ctx, schema.BatchMetricConditions{
+		metricsValues := metrics.IntValues(ctx, schema.BatchMetricConditions{
 			Name: metricsName,
 			Ids:  ids,
 		}, schema.Duration{
@@ -74,6 +75,6 @@ var Command = cli.Command{
 			Step:  step.(*model.StepEnumValue).Selected,
 		})
 
-		return display.Display(ctx, metricsValues)
+		return display.Display(ctx, metricsValues.Values)
 	},
 }
diff --git a/commands/service/list.go b/commands/service/list.go
index 8400a47..2604a6f 100644
--- a/commands/service/list.go
+++ b/commands/service/list.go
@@ -20,11 +20,12 @@ package service
 import (
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/graphql/metadata"
+
 	"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"
 )
 
@@ -47,13 +48,13 @@ var ListCommand = cli.Command{
 		var services []schema.Service
 
 		if args := ctx.Args(); len(args) == 0 {
-			services = client.Services(ctx, schema.Duration{
+			services = metadata.AllServices(ctx, schema.Duration{
 				Start: start,
 				End:   end,
 				Step:  step.(*model.StepEnumValue).Selected,
 			})
 		} else {
-			service, _ := client.SearchService(ctx, args.First())
+			service, _ := metadata.SearchService(ctx, args.First())
 			services = []schema.Service{service}
 		}
 
diff --git a/display/display.go b/display/display.go
index 48be6ab..e620673 100644
--- a/display/display.go
+++ b/display/display.go
@@ -31,10 +31,10 @@ import (
 )
 
 const (
-	JSON  string = "json"
-	YAML  string = "yaml"
-	TABLE string = "table"
-	GRAPH string = "graph"
+	JSON  = "json"
+	YAML  = "yaml"
+	TABLE = "table"
+	GRAPH = "graph"
 )
 
 // Display the object in the style specified in flag --display
diff --git a/display/graph/graph.go b/display/graph/graph.go
index 7184360..c27f835 100644
--- a/display/graph/graph.go
+++ b/display/graph/graph.go
@@ -25,11 +25,17 @@ import (
 )
 
 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))
+	if reflect.TypeOf(object) == reflect.TypeOf(map[string]float64{}) {
+		kvs := []map[string]float64{object.(map[string]float64)}
+
+		return linear.Display(kvs)
 	}
 
-	kvs := object.(map[string]float64)
+	if reflect.TypeOf(object) == reflect.TypeOf([]map[string]float64{}) {
+		kvs := object.([]map[string]float64)
+
+		return linear.Display(kvs)
+	}
 
-	return linear.Display(kvs)
+	return fmt.Errorf("type of %T is not supported to be displayed as ascii graph", reflect.TypeOf(object))
 }
diff --git a/display/graph/linear/linear.go b/display/graph/linear/linear.go
index b6e6d07..c632112 100644
--- a/display/graph/linear/linear.go
+++ b/display/graph/linear/linear.go
@@ -19,14 +19,15 @@ package linear
 
 import (
 	"context"
+	"fmt"
+	"math"
 	"strings"
 
-	"github.com/mum4k/termdash/widgetapi"
+	"github.com/mum4k/termdash/linestyle"
 
 	"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"
@@ -34,20 +35,19 @@ import (
 
 const RootID = "root"
 
-func newWidgets(inputs map[string]float64) (lineChart *linechart.LineChart, err error) {
+func newLineChart(inputs map[string]float64) (lineChart *linechart.LineChart, err error) {
 	index := 0
 
 	xLabels := map[int]string{}
-	var yValues []float64
+	yValues := make([]float64, len(inputs))
+
 	for xLabel, yValue := range inputs {
 		xLabels[index] = xLabel
+		yValues[index] = yValue
 		index++
-		yValues = append(yValues, yValue)
 	}
 
-	if lineChart, err = linechart.New(
-		linechart.YAxisAdaptive(),
-	); err != nil {
+	if lineChart, err = linechart.New(linechart.YAxisAdaptive()); err != nil {
 		return
 	}
 
@@ -56,21 +56,42 @@ func newWidgets(inputs map[string]float64) (lineChart *linechart.LineChart, err
 	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"),
-	)
+func layout(lineCharts ...*linechart.LineChart) ([]container.Option, error) {
+	cols := maxSqrt(len(lineCharts))
+
+	rows := make([][]grid.Element, int(math.Ceil(float64(len(lineCharts))/float64(cols))))
+
+	for r := 0; r < len(rows); r++ {
+		var row []grid.Element
+		for c := 0; c < cols && r*cols+c < len(lineCharts); c++ {
+			percentage := int(math.Floor(float64(100) / float64(cols)))
+			if r == len(rows)-1 {
+				percentage = int(math.Floor(float64(100) / float64(len(lineCharts)-r*cols)))
+			}
+			row = append(row, grid.ColWidthPerc(
+				int(math.Min(99, float64(percentage))),
+				grid.Widget(
+					lineCharts[r*cols+c],
+					container.Border(linestyle.Light),
+					container.BorderTitleAlignCenter(),
+					container.BorderTitle(fmt.Sprintf("#%v", r*cols+c)),
+				),
+			))
+		}
+		rows[r] = row
+	}
 
 	builder := grid.New()
-	builder.Add(widget)
+
+	for _, row := range rows {
+		percentage := int(math.Min(99, float64(100/len(rows))))
+		builder.Add(grid.RowHeightPerc(percentage, row...))
+	}
 
 	return builder.Build()
 }
 
-func Display(inputs map[string]float64) error {
+func Display(inputs []map[string]float64) error {
 	t, err := termbox.New()
 	if err != nil {
 		return err
@@ -80,26 +101,31 @@ func Display(inputs map[string]float64) error {
 	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
+	var elements []*linechart.LineChart
+
+	for _, input := range inputs {
+		w, e := newLineChart(input)
+		if e != nil {
+			return e
+		}
+		elements = append(elements, w)
 	}
 
-	gridOpts, err := gridLayout(w)
+	gridOpts, err := layout(elements...)
 	if err != nil {
 		return err
 	}
 
-	err = c.Update(RootID, gridOpts...)
+	err = c.Update(RootID, append(
+		gridOpts,
+		container.Border(linestyle.Light),
+		container.BorderTitle("PRESS Q TO QUIT"))...,
+	)
 
 	if err != nil {
 		return err
@@ -116,3 +142,7 @@ func Display(inputs map[string]float64) error {
 
 	return err
 }
+
+func maxSqrt(num int) int {
+	return int(math.Ceil(math.Sqrt(float64(num))))
+}
diff --git a/graphql/client/client.go b/graphql/client/client.go
index a0766ef..a14f2f8 100644
--- a/graphql/client/client.go
+++ b/graphql/client/client.go
@@ -19,13 +19,10 @@ package client
 
 import (
 	"context"
-	"fmt"
-	"time"
 
 	"github.com/machinebox/graphql"
 	"github.com/urfave/cli"
 
-	"github.com/apache/skywalking-cli/graphql/schema"
 	"github.com/apache/skywalking-cli/logger"
 )
 
@@ -37,6 +34,7 @@ func newClient(cliCtx *cli.Context) (client *graphql.Client) {
 	return
 }
 
+// ExecuteQuery executes the `request` and parse to the `response`, returning `error` if there is any.
 func ExecuteQuery(cliCtx *cli.Context, request *graphql.Request, response interface{}) error {
 	client := newClient(cliCtx)
 	ctx := context.Background()
@@ -44,151 +42,9 @@ func ExecuteQuery(cliCtx *cli.Context, request *graphql.Request, response interf
 	return err
 }
 
+// ExecuteQuery executes the `request` and parse to the `response`, panic if there is any `error`.
 func ExecuteQueryOrFail(cliCtx *cli.Context, request *graphql.Request, response interface{}) {
-	client := newClient(cliCtx)
-	ctx := context.Background()
-	if err := client.Run(ctx, request, response); err != nil {
-		logger.Log.Fatalln(err)
-	}
-}
-
-func Services(cliCtx *cli.Context, duration schema.Duration) []schema.Service {
-	var response map[string][]schema.Service
-	request := graphql.NewRequest(`
-		query ($duration: Duration!) {
-			services: getAllServices(duration: $duration) {
-				id name
-			}
-		}
-	`)
-	request.Var("duration", duration)
-
-	ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["services"]
-}
-
-func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int) []schema.Endpoint {
-	var response map[string][]schema.Endpoint
-	request := graphql.NewRequest(`
-		query ($keyword: String!, $serviceId: ID!, $limit: Int!) {
-			endpoints: searchEndpoint(keyword: $keyword, serviceId: $serviceId, limit: $limit) {
-				id name
-			}
-		}
-	`)
-	request.Var("serviceId", serviceID)
-	request.Var("keyword", keyword)
-	request.Var("limit", limit)
-
-	ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["endpoints"]
-}
-
-func GetEndpointInfo(cliCtx *cli.Context, endpointID string) schema.Endpoint {
-	var response map[string]schema.Endpoint
-	request := graphql.NewRequest(`
-		query ($endpointId: ID!) {
-			endpoint: getEndpointInfo(endpointId: $endpointId) {
-				id name
-			}
-		}
-	`)
-	request.Var("endpointId", endpointID)
-
-	ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["endpoint"]
-}
-
-func Instances(cliCtx *cli.Context, serviceID string, duration schema.Duration) []schema.ServiceInstance {
-	var response map[string][]schema.ServiceInstance
-	request := graphql.NewRequest(`
-		query ($serviceId: ID!, $duration: Duration!) {
-			instances: getServiceInstances(duration: $duration, serviceId: $serviceId) {
-				id
-				name
-				language
-				instanceUUID
-				attributes {
-					name
-					value
-				}
-			}
-		}
-	`)
-	request.Var("serviceId", serviceID)
-	request.Var("duration", duration)
-
-	ExecuteQueryOrFail(cliCtx, request, &response)
-	return response["instances"]
-}
-
-func SearchService(cliCtx *cli.Context, serviceCode string) (service schema.Service, err error) {
-	var response map[string]schema.Service
-	request := graphql.NewRequest(`
-		query searchService($serviceCode: String!) {
-			service: searchService(serviceCode: $serviceCode) {
-				id name
-			}
-		}
-	`)
-	request.Var("serviceCode", serviceCode)
-
-	ExecuteQueryOrFail(cliCtx, request, &response)
-	service = response["service"]
-	if service.ID == "" {
-		return service, fmt.Errorf("no such service [%s]", serviceCode)
-	}
-	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)
-
-	ExecuteQueryOrFail(ctx, request, &response)
-
-	values := metricsToMap(duration, response["metrics"].Values)
-
-	return values
-}
-
-func IntValues(ctx *cli.Context, condition schema.BatchMetricConditions, duration schema.Duration) []*schema.KVInt {
-	var response map[string]schema.IntValues
-
-	request := graphql.NewRequest(`
-		query ($metric: BatchMetricConditions!, $duration: Duration!) {
-			metrics: getValues(metric: $metric, duration: $duration) {
-				values { id value }
-			}
-		}
-	`)
-	request.Var("metric", condition)
-	request.Var("duration", duration)
-
-	ExecuteQueryOrFail(ctx, request, &response)
-
-	return response["metrics"].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 {
+	if err := ExecuteQuery(cliCtx, request, response); 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/graphql/metadata/metadata.go b/graphql/metadata/metadata.go
index 55a1bc7..93b6875 100644
--- a/graphql/metadata/metadata.go
+++ b/graphql/metadata/metadata.go
@@ -18,6 +18,8 @@
 package metadata
 
 import (
+	"fmt"
+
 	"github.com/machinebox/graphql"
 	"github.com/urfave/cli"
 
@@ -25,7 +27,97 @@ import (
 	"github.com/apache/skywalking-cli/graphql/schema"
 )
 
+func AllServices(cliCtx *cli.Context, duration schema.Duration) []schema.Service {
+	var response map[string][]schema.Service
+	request := graphql.NewRequest(`
+		query ($duration: Duration!) {
+			services: getAllServices(duration: $duration) {
+				id name
+			}
+		}
+	`)
+	request.Var("duration", duration)
+
+	client.ExecuteQueryOrFail(cliCtx, request, &response)
+	return response["services"]
+}
+
+func SearchService(cliCtx *cli.Context, serviceCode string) (service schema.Service, err error) {
+	var response map[string]schema.Service
+	request := graphql.NewRequest(`
+		query searchService($serviceCode: String!) {
+			service: searchService(serviceCode: $serviceCode) {
+				id name
+			}
+		}
+	`)
+	request.Var("serviceCode", serviceCode)
+
+	client.ExecuteQueryOrFail(cliCtx, request, &response)
+	service = response["service"]
+	if service.ID == "" {
+		return service, fmt.Errorf("no such service [%s]", serviceCode)
+	}
+	return service, nil
+}
+
+func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int) []schema.Endpoint {
+	var response map[string][]schema.Endpoint
+	request := graphql.NewRequest(`
+		query ($keyword: String!, $serviceId: ID!, $limit: Int!) {
+			endpoints: searchEndpoint(keyword: $keyword, serviceId: $serviceId, limit: $limit) {
+				id name
+			}
+		}
+	`)
+	request.Var("serviceId", serviceID)
+	request.Var("keyword", keyword)
+	request.Var("limit", limit)
+
+	client.ExecuteQueryOrFail(cliCtx, request, &response)
+	return response["endpoints"]
+}
+
+func EndpointInfo(cliCtx *cli.Context, endpointID string) schema.Endpoint {
+	var response map[string]schema.Endpoint
+	request := graphql.NewRequest(`
+		query ($endpointId: ID!) {
+			endpoint: getEndpointInfo(endpointId: $endpointId) {
+				id name
+			}
+		}
+	`)
+	request.Var("endpointId", endpointID)
+
+	client.ExecuteQueryOrFail(cliCtx, request, &response)
+	return response["endpoint"]
+}
+
+func Instances(cliCtx *cli.Context, serviceID string, duration schema.Duration) []schema.ServiceInstance {
+	var response map[string][]schema.ServiceInstance
+	request := graphql.NewRequest(`
+		query ($serviceId: ID!, $duration: Duration!) {
+			instances: getServiceInstances(duration: $duration, serviceId: $serviceId) {
+				id
+				name
+				language
+				instanceUUID
+				attributes {
+					name
+					value
+				}
+			}
+		}
+	`)
+	request.Var("serviceId", serviceID)
+	request.Var("duration", duration)
+
+	client.ExecuteQueryOrFail(cliCtx, request, &response)
+	return response["instances"]
+}
+
 func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
+	var response map[string]schema.TimeInfo
 	request := graphql.NewRequest(`
 		query {
 			timeInfo: getTimeInfo {
@@ -34,7 +126,6 @@ func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
 		}
 	`)
 
-	var response map[string]schema.TimeInfo
 	if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
 		return schema.TimeInfo{}, err
 	}
diff --git a/graphql/metrics/metrics.go b/graphql/metrics/metrics.go
new file mode 100644
index 0000000..c086e51
--- /dev/null
+++ b/graphql/metrics/metrics.go
@@ -0,0 +1,82 @@
+// 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/machinebox/graphql"
+	"github.com/urfave/cli"
+
+	"github.com/apache/skywalking-cli/graphql/client"
+
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
+
+func IntValues(ctx *cli.Context, condition schema.BatchMetricConditions, duration schema.Duration) schema.IntValues {
+	var response map[string]schema.IntValues
+
+	request := graphql.NewRequest(`
+		query ($metric: BatchMetricConditions!, $duration: Duration!) {
+			metrics: getValues(metric: $metric, duration: $duration) {
+				values { id value }
+			}
+		}
+	`)
+	request.Var("metric", condition)
+	request.Var("duration", duration)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["metrics"]
+}
+
+func LinearIntValues(ctx *cli.Context, condition schema.MetricCondition, duration schema.Duration) schema.IntValues {
+	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)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["metrics"]
+}
+
+func MultipleLinearIntValues(ctx *cli.Context, condition schema.MetricCondition, numOfLinear int, duration schema.Duration) []schema.IntValues {
+	request := graphql.NewRequest(`
+		query ($metric: MetricCondition!, $numOfLinear: Int!, $duration: Duration!) {
+			metrics: getMultipleLinearIntValues(metric: $metric, numOfLinear: $numOfLinear, duration: $duration) {
+				values { value }
+			}
+		}
+	`)
+	request.Var("metric", condition)
+	request.Var("numOfLinear", numOfLinear)
+	request.Var("duration", duration)
+
+	var response map[string][]schema.IntValues
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["metrics"]
+}
diff --git a/graphql/metadata/metadata.go b/graphql/utils/adapter.go
similarity index 61%
copy from graphql/metadata/metadata.go
copy to graphql/utils/adapter.go
index 55a1bc7..baec147 100644
--- a/graphql/metadata/metadata.go
+++ b/graphql/utils/adapter.go
@@ -15,28 +15,31 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package metadata
+package utils
 
 import (
-	"github.com/machinebox/graphql"
-	"github.com/urfave/cli"
+	"time"
 
-	"github.com/apache/skywalking-cli/graphql/client"
 	"github.com/apache/skywalking-cli/graphql/schema"
+	"github.com/apache/skywalking-cli/logger"
 )
 
-func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
-	request := graphql.NewRequest(`
-		query {
-			timeInfo: getTimeInfo {
-				timezone, currentTimestamp
-			}
-		}
-	`)
-
-	var response map[string]schema.TimeInfo
-	if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
-		return schema.TimeInfo{}, err
+type IntValues schema.IntValues
+
+func MetricsToMap(duration schema.Duration, intValues schema.IntValues) map[string]float64 {
+	kvInts := intValues.Values
+	values := map[string]float64{}
+	format := StepFormats[duration.Step]
+	startTime, err := time.Parse(format, duration.Start)
+
+	if err != nil {
+		logger.Log.Fatalln(err)
 	}
-	return response["timeInfo"], nil
+
+	step := 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/graphql/schema/constants.go b/graphql/utils/constants.go
similarity index 64%
rename from graphql/schema/constants.go
rename to graphql/utils/constants.go
index f146338..b4f0f3d 100644
--- a/graphql/schema/constants.go
+++ b/graphql/utils/constants.go
@@ -15,24 +15,28 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package schema
+package utils
 
-import "time"
+import (
+	"time"
+
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
 
 // 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",
+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",
 }
 
 // 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,
+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,
 }
diff --git a/swctl/main.go b/swctl/main.go
index 2efdee7..6c39a23 100644
--- a/swctl/main.go
+++ b/swctl/main.go
@@ -21,10 +21,9 @@ import (
 	"io/ioutil"
 	"os"
 
-	"github.com/apache/skywalking-cli/commands/endpoint"
-	linearMetrics "github.com/apache/skywalking-cli/commands/metrics/linear"
-	singleMetrics "github.com/apache/skywalking-cli/commands/metrics/single"
+	"github.com/apache/skywalking-cli/commands/metrics"
 
+	"github.com/apache/skywalking-cli/commands/endpoint"
 	"github.com/apache/skywalking-cli/commands/instance"
 
 	"github.com/sirupsen/logrus"
@@ -81,11 +80,10 @@ func main() {
 	}
 
 	app.Commands = []cli.Command{
-		service.Command,
-		instance.Command,
-		linearMetrics.Command,
-		singleMetrics.Command,
 		endpoint.Command,
+		instance.Command,
+		service.Command,
+		metrics.Command,
 	}
 
 	app.Before = interceptor.BeforeChain([]cli.BeforeFunc{