You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ho...@apache.org on 2020/12/10 01:14:21 UTC

[skywalking-cli] branch master updated: Fix the bug that can't query JVM instance metrics (#79)

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

hoshea 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 2b7fae8  Fix the bug that can't query JVM instance metrics (#79)
2b7fae8 is described below

commit 2b7fae8a189e517b43e374fb9ddbf3a564969037
Author: Hoshea Jiang <fg...@gmail.com>
AuthorDate: Thu Dec 10 09:14:12 2020 +0800

    Fix the bug that can't query JVM instance metrics (#79)
    
    * Add `--scope` for `metrics top`
    * Refactor the way to infer `scope`
    * add unit test for parseScope
---
 README.md                                          |  27 ++++-
 commands/flags/{metrics.go => entity.go}           |  35 +++---
 commands/flags/metrics.go                          |  26 ++---
 commands/interceptor/entity.go                     |  71 ++++++++++++
 commands/interceptor/entity_test.go                | 126 +++++++++++++++++++++
 commands/interceptor/scope.go                      |  45 --------
 commands/interceptor/scope_test.go                 |  80 -------------
 commands/metrics/aggregation/topn.go               |   2 +-
 commands/metrics/linear/linear-metrics.go          |  26 +----
 commands/metrics/linear/multiple-linear-metrics.go |  19 +---
 commands/metrics/single/single-metrics.go          |  26 +----
 commands/metrics/thermodynamic/thermodynamic.go    |  18 +--
 12 files changed, 272 insertions(+), 229 deletions(-)

diff --git a/README.md b/README.md
index 1b51908..3af15ca 100644
--- a/README.md
+++ b/README.md
@@ -160,7 +160,7 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 
 <details>
 
-<summary>metrics linear [--start=start-time] [--end=end-time] --name=metrics-name --service=service-name [--instance=instance-name] [--endpoint=endpoint-name] [--isNormal=true/false]</summary>
+<summary>metrics linear [--start=start-time] [--end=end-time] --name=metrics-name [--service=service-name] [--instance=instance-name] [--endpoint=endpoint-name] [--isNormal=true/false] [--destService=dest-service-name] [--destInstance=dest-instance-name] [--destEndpoint=dest-endpoint-name] [--isDestNormal=true/false]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -169,6 +169,10 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 | `--instance` | The name of the service instance. | "" |
 | `--endpoint` | The name of the endpoint. | "" |
 | `--isNormal` | Set the service to normal or unnormal. | `true` |
+| `--destService` | The name of the destination service. | "" |
+| `--destInstance` | The name of the destination instance. | "" |
+| `--destEndpoint` | The name of the destination endpoint. | "" |
+| `--isDestNormal` | Set the destination service to normal or unnormal. | `true` |
 | `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
 
@@ -178,7 +182,7 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 
 <details>
 
-<summary>metrics multiple-linear [--start=start-time] [--end=end-time] --name=metrics-name [--service=service-name] [--num=number-of-linear-metrics] [--instance=instance-name] [--endpoint=endpoint-name] [--isNormal=true/false]</summary>
+<summary>metrics multiple-linear [--start=start-time] [--end=end-time] --name=metrics-name [--service=service-name] [--num=number-of-linear-metrics] [--instance=instance-name] [--endpoint=endpoint-name] [--isNormal=true/false] [--destService=dest-service-name] [--destInstance=dest-instance-name] [--destEndpoint=dest-endpoint-name] [--isDestNormal=true/false]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -188,6 +192,10 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 | `--instance` | The name of the service instance. | "" |
 | `--endpoint` | The name of the endpoint. | "" |
 | `--isNormal` | Set the service to normal or unnormal. | `true` |
+| `--destService` | The name of the destination service. | "" |
+| `--destInstance` | The name of the destination instance. | "" |
+| `--destEndpoint` | The name of the destination endpoint. | "" |
+| `--isDestNormal` | Set the destination service to normal or unnormal. | `true` |
 | `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
 
@@ -197,7 +205,7 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 
 <details>
 
-<summary>metrics single [--start=start-time] [--end=end-time] --name=metrics-name --service=service-name [--instance=instance-name] [--endpoint=endpoint-name] [--isNormal=true/false]</summary>
+<summary>metrics single [--start=start-time] [--end=end-time] --name=metrics-name --service=service-name [--instance=instance-name] [--endpoint=endpoint-name] [--isNormal=true/false] [--destService=dest-service-name] [--destInstance=dest-instance-name] [--destEndpoint=dest-endpoint-name] [--isDestNormal=true/false]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -206,6 +214,10 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 | `--instance` | The name of the service instance. | "" |
 | `--endpoint` | The name of the endpoint. | "" |
 | `--isNormal` | Set the service to normal or unnormal. | `true` |
+| `--destService` | The name of the destination service. | "" |
+| `--destInstance` | The name of the destination instance. | "" |
+| `--destEndpoint` | The name of the destination endpoint. | "" |
+| `--isDestNormal` | Set the destination service to normal or unnormal. | `true` |
 | `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
 
@@ -215,7 +227,7 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 
 <details>
 
-<summary>metrics top 5 [--start=start-time] [--end=end-time] --name=metrics-name [--service=parent-service] [--order=DES] [--isNormal=true/false]</summary>
+<summary>metrics top 5 [--start=start-time] [--end=end-time] --name=metrics-name [--service=parent-service] [--order=DES] [--scope=Service/ServiceInstance/Endpoint] [--isNormal=true/false]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
@@ -223,6 +235,7 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 | `--name` | Metrics name, defined in [OAL](https://github.com/apache/skywalking/blob/master/oap-server/server-bootstrap/src/main/resources/oal/core.oal), such as `service_sla`, etc. |
 | `--service` | The name of the parent service, could be null if query the global top N. | "" |
 | `--order` | The order of metrics, `DES` or `ASC`. |`DES`|
+| `--scope` | The scope of the metrics entity, only accept `Service`/`ServiceInstance`/`Endpoint`, ignore others due to those are pointless. |`Service`|
 | `--isNormal` | Set the service to normal or unnormal. | `true` |
 | `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
@@ -233,11 +246,15 @@ Ascii Graph, like coloring in terminal, so please use `json`  or `yaml` instead.
 
 <details>
 
-<summary>metrics thermodynamic [--name=metrics-name]</summary>
+<summary>metrics thermodynamic [--name=metrics-name] [--destService=dest-service-name] [--destInstance=dest-instance-name] [--destEndpoint=dest-endpoint-name] [--isDestNormal=true/false]</summary>
 
 | option | description | default |
 | :--- | :--- | :--- |
 | `--name` | Metrics name that ends with `_heatmap`, defined in [OAL](https://github.com/apache/skywalking/blob/master/oap-server/server-bootstrap/src/main/resources/oal/core.oal), such as `all_heatmap`, etc. | `all_heatmap` |
+| `--destService` | The name of the destination service. | "" |
+| `--destInstance` | The name of the destination instance. | "" |
+| `--destEndpoint` | The name of the destination endpoint. | "" |
+| `--isDestNormal` | Set the destination service to normal or unnormal. | `true` |
 | `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
 | `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
 
diff --git a/commands/flags/metrics.go b/commands/flags/entity.go
similarity index 65%
copy from commands/flags/metrics.go
copy to commands/flags/entity.go
index 151c4ae..eefc11c 100644
--- a/commands/flags/metrics.go
+++ b/commands/flags/entity.go
@@ -21,34 +21,41 @@ import (
 	"github.com/urfave/cli"
 )
 
-// MetricsFlags can be reused in several metrics commands.
-var MetricsFlags = []cli.Flag{
+// EntityFlags are attributes of Entity in the metrics v2 protocol.
+var EntityFlags = []cli.Flag{
 	cli.StringFlag{
-		Name:     "name",
-		Usage:    "metrics `name`, which should be defined in OAL script",
-		Required: true,
+		Name:     "instance",
+		Usage:    "the name of the service instance",
+		Value:    "",
+		Required: false,
 	},
 	cli.StringFlag{
-		Name:     "service",
-		Usage:    "the name of the service",
+		Name:     "endpoint",
+		Usage:    "the name of the endpoint",
 		Value:    "",
 		Required: false,
 	},
 	cli.StringFlag{
-		Name:     "instance",
-		Usage:    "the name of service instance",
+		Name:     "destService",
+		Usage:    "the name of the destination endpoint",
 		Value:    "",
 		Required: false,
 	},
+	cli.BoolTFlag{
+		Name:     "isDestNormal",
+		Usage:    "set the destination service to normal or unnormal",
+		Required: false,
+	},
 	cli.StringFlag{
-		Name:     "endpoint",
-		Usage:    "the name of endpoint",
+		Name:     "destInstance",
+		Usage:    "the name of the destination endpoint",
 		Value:    "",
 		Required: false,
 	},
-	cli.BoolTFlag{
-		Name:     "isNormal",
-		Usage:    "set the service to normal or unnormal",
+	cli.StringFlag{
+		Name:     "destEndpoint",
+		Usage:    "the name of the destination endpoint",
+		Value:    "",
 		Required: false,
 	},
 }
diff --git a/commands/flags/metrics.go b/commands/flags/metrics.go
index 151c4ae..cda10a3 100644
--- a/commands/flags/metrics.go
+++ b/commands/flags/metrics.go
@@ -18,10 +18,13 @@
 package flags
 
 import (
+	"github.com/apache/skywalking-cli/commands/model"
+	"github.com/apache/skywalking-cli/graphql/schema"
+
 	"github.com/urfave/cli"
 )
 
-// MetricsFlags can be reused in several metrics commands.
+// MetricsFlags can be reused in metrics commands.
 var MetricsFlags = []cli.Flag{
 	cli.StringFlag{
 		Name:     "name",
@@ -34,21 +37,18 @@ var MetricsFlags = []cli.Flag{
 		Value:    "",
 		Required: false,
 	},
-	cli.StringFlag{
-		Name:     "instance",
-		Usage:    "the name of service instance",
-		Value:    "",
-		Required: false,
-	},
-	cli.StringFlag{
-		Name:     "endpoint",
-		Usage:    "the name of endpoint",
-		Value:    "",
-		Required: false,
-	},
 	cli.BoolTFlag{
 		Name:     "isNormal",
 		Usage:    "set the service to normal or unnormal",
 		Required: false,
 	},
+	cli.GenericFlag{
+		Name:  "scope",
+		Usage: "the scope of the metrics entity",
+		Value: &model.ScopeEnumValue{
+			Enum:     schema.AllScope,
+			Default:  schema.ScopeService,
+			Selected: schema.ScopeService,
+		},
+	},
 }
diff --git a/commands/interceptor/entity.go b/commands/interceptor/entity.go
new file mode 100644
index 0000000..f9544ca
--- /dev/null
+++ b/commands/interceptor/entity.go
@@ -0,0 +1,71 @@
+// 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 interceptor
+
+import (
+	"github.com/apache/skywalking-cli/graphql/schema"
+
+	"github.com/urfave/cli"
+)
+
+func ParseEntity(ctx *cli.Context) *schema.Entity {
+	service := ctx.String("service")
+	normal := ctx.BoolT("isNormal")
+	instance := ctx.String("instance")
+	endpoint := ctx.String("endpoint")
+
+	destService := ctx.String("destService")
+	destNormal := ctx.BoolT("isDestNormal")
+	destInstance := ctx.String("destServiceInstance")
+	destEndpoint := ctx.String("destEndpoint")
+
+	entity := &schema.Entity{
+		ServiceName:             &service,
+		Normal:                  &normal,
+		ServiceInstanceName:     &instance,
+		EndpointName:            &endpoint,
+		DestServiceName:         &destService,
+		DestNormal:              &destNormal,
+		DestServiceInstanceName: &destInstance,
+		DestEndpointName:        &destEndpoint,
+	}
+	entity.Scope = parseScope(entity)
+
+	return entity
+}
+
+// parseScope defines the scope based on the input parameters.
+func parseScope(entity *schema.Entity) schema.Scope {
+	scope := schema.ScopeAll
+
+	if *entity.DestEndpointName != "" {
+		scope = schema.ScopeEndpointRelation
+	} else if *entity.DestServiceInstanceName != "" {
+		scope = schema.ScopeServiceInstanceRelation
+	} else if *entity.DestServiceName != "" {
+		scope = schema.ScopeServiceRelation
+	} else if *entity.EndpointName != "" {
+		scope = schema.ScopeEndpoint
+	} else if *entity.ServiceInstanceName != "" {
+		scope = schema.ScopeServiceInstance
+	} else if *entity.ServiceName != "" {
+		scope = schema.ScopeService
+	}
+
+	return scope
+}
diff --git a/commands/interceptor/entity_test.go b/commands/interceptor/entity_test.go
new file mode 100644
index 0000000..da3a03a
--- /dev/null
+++ b/commands/interceptor/entity_test.go
@@ -0,0 +1,126 @@
+// 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 interceptor
+
+import (
+	"testing"
+
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
+
+func Test_parseScope(t *testing.T) {
+	empty := ""
+	nonEmpty := "test"
+	tests := []struct {
+		name string
+		args *schema.Entity
+		want schema.Scope
+	}{
+		{
+			name: "all of names are empty",
+			args: &schema.Entity{
+				ServiceName:             &empty,
+				ServiceInstanceName:     &empty,
+				EndpointName:            &empty,
+				DestServiceName:         &empty,
+				DestServiceInstanceName: &empty,
+				DestEndpointName:        &empty,
+			},
+			want: schema.ScopeAll,
+		},
+		{
+			name: "all of names are not empty",
+			args: &schema.Entity{
+				ServiceName:             &nonEmpty,
+				ServiceInstanceName:     &nonEmpty,
+				EndpointName:            &nonEmpty,
+				DestServiceName:         &nonEmpty,
+				DestServiceInstanceName: &nonEmpty,
+				DestEndpointName:        &nonEmpty,
+			},
+			want: schema.ScopeEndpointRelation,
+		},
+		{
+			name: "only serviceName is not empty",
+			args: &schema.Entity{
+				ServiceName:             &nonEmpty,
+				ServiceInstanceName:     &empty,
+				EndpointName:            &empty,
+				DestServiceName:         &empty,
+				DestServiceInstanceName: &empty,
+				DestEndpointName:        &empty,
+			},
+			want: schema.ScopeService,
+		},
+		{
+			name: "instanceName is not empty",
+			args: &schema.Entity{
+				ServiceName:             &nonEmpty,
+				ServiceInstanceName:     &nonEmpty,
+				EndpointName:            &empty,
+				DestServiceName:         &empty,
+				DestServiceInstanceName: &empty,
+				DestEndpointName:        &empty,
+			},
+			want: schema.ScopeServiceInstance,
+		},
+		{
+			name: "endpointName is not empty",
+			args: &schema.Entity{
+				ServiceName:             &nonEmpty,
+				ServiceInstanceName:     &empty,
+				EndpointName:            &nonEmpty,
+				DestServiceName:         &empty,
+				DestServiceInstanceName: &empty,
+				DestEndpointName:        &empty,
+			},
+			want: schema.ScopeEndpoint,
+		},
+		{
+			name: "destService is not empty",
+			args: &schema.Entity{
+				ServiceName:             &nonEmpty,
+				ServiceInstanceName:     &empty,
+				EndpointName:            &empty,
+				DestServiceName:         &nonEmpty,
+				DestServiceInstanceName: &empty,
+				DestEndpointName:        &empty,
+			},
+			want: schema.ScopeServiceRelation,
+		},
+		{
+			name: "destInstance is not empty",
+			args: &schema.Entity{
+				ServiceName:             &nonEmpty,
+				ServiceInstanceName:     &nonEmpty,
+				EndpointName:            &empty,
+				DestServiceName:         &nonEmpty,
+				DestServiceInstanceName: &nonEmpty,
+				DestEndpointName:        &empty,
+			},
+			want: schema.ScopeServiceInstanceRelation,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := parseScope(tt.args); got != tt.want {
+				t.Errorf("parseScope() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/commands/interceptor/scope.go b/commands/interceptor/scope.go
deleted file mode 100644
index 6ecd7bc..0000000
--- a/commands/interceptor/scope.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 interceptor
-
-import (
-	"strings"
-
-	"github.com/apache/skywalking-cli/graphql/schema"
-)
-
-// ParseScope defines the scope according to name's prefix.
-func ParseScope(name string) schema.Scope {
-	ret := schema.ScopeAll
-
-	if strings.HasPrefix(name, "service_relation") {
-		ret = schema.ScopeServiceRelation
-	} else if strings.HasPrefix(name, "service_instance_relation") {
-		ret = schema.ScopeServiceInstanceRelation
-	} else if strings.HasPrefix(name, "service_instance") {
-		ret = schema.ScopeServiceInstance
-	} else if strings.HasPrefix(name, "service_") || strings.HasPrefix(name, "database_") {
-		ret = schema.ScopeService
-	} else if strings.HasPrefix(name, "endpoint_relation") {
-		ret = schema.ScopeEndpointRelation
-	} else if strings.HasPrefix(name, "endpoint_") {
-		ret = schema.ScopeEndpoint
-	}
-
-	return ret
-}
diff --git a/commands/interceptor/scope_test.go b/commands/interceptor/scope_test.go
deleted file mode 100644
index 8a1d87f..0000000
--- a/commands/interceptor/scope_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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 interceptor
-
-import (
-	"testing"
-
-	"github.com/apache/skywalking-cli/graphql/schema"
-)
-
-func TestParseScope(t *testing.T) {
-	tests := []struct {
-		name        string
-		wantedScope schema.Scope
-	}{
-		{
-			name:        "",
-			wantedScope: schema.ScopeAll,
-		},
-		{
-			name:        "all_percentile",
-			wantedScope: schema.ScopeAll,
-		},
-		{
-			name:        "all_heatmap",
-			wantedScope: schema.ScopeAll,
-		},
-		{
-			name:        "service_resp_time",
-			wantedScope: schema.ScopeService,
-		},
-		{
-			name:        "service_percentile",
-			wantedScope: schema.ScopeService,
-		},
-		{
-			name:        "service_relation_server_percentile ",
-			wantedScope: schema.ScopeServiceRelation,
-		},
-		{
-			name:        "service_instance_relation_client_cpm",
-			wantedScope: schema.ScopeServiceInstanceRelation,
-		},
-		{
-			name:        "service_instance_resp_time",
-			wantedScope: schema.ScopeServiceInstance,
-		},
-		{
-			name:        "endpoint_cpm",
-			wantedScope: schema.ScopeEndpoint,
-		},
-		{
-			name:        "endpoint_relation_resp_time",
-			wantedScope: schema.ScopeEndpointRelation,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			gotScope := ParseScope(tt.name)
-			if gotScope != tt.wantedScope {
-				t.Errorf("ParseScope() got scope = %v, wanted scope %v", gotScope, tt.wantedScope)
-			}
-		})
-	}
-}
diff --git a/commands/metrics/aggregation/topn.go b/commands/metrics/aggregation/topn.go
index 0745691..a3505cf 100644
--- a/commands/metrics/aggregation/topn.go
+++ b/commands/metrics/aggregation/topn.go
@@ -62,7 +62,7 @@ var TopN = cli.Command{
 
 		metricsName := ctx.String("name")
 		normal := ctx.BoolT("isNormal")
-		scope := interceptor.ParseScope(metricsName)
+		scope := ctx.Generic("scope").(*model.ScopeEnumValue).Selected
 		order := ctx.Generic("order").(*model.OrderEnumValue).Selected
 		topN := 5
 		parentService := ctx.String("service")
diff --git a/commands/metrics/linear/linear-metrics.go b/commands/metrics/linear/linear-metrics.go
index 36711c3..717943c 100644
--- a/commands/metrics/linear/linear-metrics.go
+++ b/commands/metrics/linear/linear-metrics.go
@@ -18,8 +18,6 @@
 package linear
 
 import (
-	"fmt"
-
 	"github.com/apache/skywalking-cli/commands/flags"
 	"github.com/apache/skywalking-cli/commands/interceptor"
 	"github.com/apache/skywalking-cli/commands/model"
@@ -38,6 +36,7 @@ var Single = cli.Command{
 	Flags: flags.Flags(
 		flags.DurationFlags,
 		flags.MetricsFlags,
+		flags.EntityFlags,
 	),
 	Before: interceptor.BeforeChain([]cli.BeforeFunc{
 		interceptor.TimezoneInterceptor,
@@ -49,18 +48,7 @@ var Single = cli.Command{
 		step := ctx.Generic("step")
 
 		metricsName := ctx.String("name")
-		serviceName := ctx.String("service")
-		normal := ctx.BoolT("isNormal")
-		instanceName := ctx.String("instance")
-		endpointName := ctx.String("endpoint")
-		scope := interceptor.ParseScope(metricsName)
-
-		if serviceName == "" {
-			return fmt.Errorf("the name of service should be specified")
-		}
-		if scope == schema.ScopeAll {
-			return fmt.Errorf("this command cannot be used to query `All` scope metrics")
-		}
+		entity := interceptor.ParseEntity(ctx)
 
 		duration := schema.Duration{
 			Start: start,
@@ -69,14 +57,8 @@ var Single = cli.Command{
 		}
 
 		metricsValues := metrics.LinearIntValues(ctx, schema.MetricsCondition{
-			Name: metricsName,
-			Entity: &schema.Entity{
-				Scope:               scope,
-				ServiceName:         &serviceName,
-				Normal:              &normal,
-				ServiceInstanceName: &instanceName,
-				EndpointName:        &endpointName,
-			},
+			Name:   metricsName,
+			Entity: entity,
 		}, duration)
 
 		return display.Display(ctx, &displayable.Displayable{Data: utils.MetricsValuesToMap(duration, metricsValues)})
diff --git a/commands/metrics/linear/multiple-linear-metrics.go b/commands/metrics/linear/multiple-linear-metrics.go
index e9ded0d..d5165cf 100644
--- a/commands/metrics/linear/multiple-linear-metrics.go
+++ b/commands/metrics/linear/multiple-linear-metrics.go
@@ -39,6 +39,7 @@ var Multiple = cli.Command{
 	Flags: flags.Flags(
 		flags.DurationFlags,
 		flags.MetricsFlags,
+		flags.EntityFlags,
 		[]cli.Flag{
 			cli.StringFlag{
 				Name:     "labels",
@@ -58,14 +59,10 @@ var Multiple = cli.Command{
 		step := ctx.Generic("step")
 
 		metricsName := ctx.String("name")
-		serviceName := ctx.String("service")
-		normal := ctx.BoolT("isNormal")
-		instanceName := ctx.String("instance")
-		endpointName := ctx.String("endpoint")
 		labels := ctx.String("labels")
-		scope := interceptor.ParseScope(metricsName)
+		entity := interceptor.ParseEntity(ctx)
 
-		if serviceName == "" && scope != schema.ScopeAll {
+		if *entity.ServiceName == "" && entity.Scope != schema.ScopeAll {
 			return fmt.Errorf("the name of service should be specified when metrics' scope is not `All`")
 		}
 
@@ -76,14 +73,8 @@ var Multiple = cli.Command{
 		}
 
 		metricsValuesArray := metrics.MultipleLinearIntValues(ctx, schema.MetricsCondition{
-			Name: metricsName,
-			Entity: &schema.Entity{
-				Scope:               scope,
-				ServiceName:         &serviceName,
-				Normal:              &normal,
-				ServiceInstanceName: &instanceName,
-				EndpointName:        &endpointName,
-			},
+			Name:   metricsName,
+			Entity: interceptor.ParseEntity(ctx),
 		}, strings.Split(labels, ","), duration)
 
 		reshaped := utils.MetricsValuesArrayToMap(duration, metricsValuesArray)
diff --git a/commands/metrics/single/single-metrics.go b/commands/metrics/single/single-metrics.go
index d407fa4..05469ec 100644
--- a/commands/metrics/single/single-metrics.go
+++ b/commands/metrics/single/single-metrics.go
@@ -18,8 +18,6 @@
 package single
 
 import (
-	"fmt"
-
 	"github.com/apache/skywalking-cli/commands/flags"
 	"github.com/apache/skywalking-cli/commands/interceptor"
 	"github.com/apache/skywalking-cli/commands/model"
@@ -37,6 +35,7 @@ var Command = cli.Command{
 	Flags: flags.Flags(
 		flags.DurationFlags,
 		flags.MetricsFlags,
+		flags.EntityFlags,
 	),
 	Before: interceptor.BeforeChain([]cli.BeforeFunc{
 		interceptor.TimezoneInterceptor,
@@ -48,18 +47,7 @@ var Command = cli.Command{
 		step := ctx.Generic("step")
 
 		metricsName := ctx.String("name")
-		serviceName := ctx.String("service")
-		normal := ctx.BoolT("isNormal")
-		instanceName := ctx.String("instance")
-		endpointName := ctx.String("endpoint")
-		scope := interceptor.ParseScope(metricsName)
-
-		if serviceName == "" {
-			return fmt.Errorf("the name of service should be specified")
-		}
-		if scope == schema.ScopeAll {
-			return fmt.Errorf("this command cannot be used to query `All` scope metrics")
-		}
+		entity := interceptor.ParseEntity(ctx)
 
 		duration := schema.Duration{
 			Start: start,
@@ -68,14 +56,8 @@ var Command = cli.Command{
 		}
 
 		metricsValue := metrics.IntValues(ctx, schema.MetricsCondition{
-			Name: metricsName,
-			Entity: &schema.Entity{
-				Scope:               scope,
-				ServiceName:         &serviceName,
-				Normal:              &normal,
-				ServiceInstanceName: &instanceName,
-				EndpointName:        &endpointName,
-			},
+			Name:   metricsName,
+			Entity: entity,
 		}, duration)
 
 		return display.Display(ctx, &displayable.Displayable{Data: metricsValue})
diff --git a/commands/metrics/thermodynamic/thermodynamic.go b/commands/metrics/thermodynamic/thermodynamic.go
index d865422..63d3f94 100644
--- a/commands/metrics/thermodynamic/thermodynamic.go
+++ b/commands/metrics/thermodynamic/thermodynamic.go
@@ -35,14 +35,8 @@ var Command = cli.Command{
 	Usage:   "Query thermodynamic metrics defined in backend OAL",
 	Flags: flags.Flags(
 		flags.DurationFlags,
-		[]cli.Flag{
-			cli.StringFlag{
-				Name:     "name",
-				Usage:    "metrics `name`, which should be defined in OAL script",
-				Value:    "all_heatmap",
-				Required: false,
-			},
-		},
+		flags.MetricsFlags,
+		flags.EntityFlags,
 	),
 	Before: interceptor.BeforeChain([]cli.BeforeFunc{
 		interceptor.TimezoneInterceptor,
@@ -54,7 +48,7 @@ var Command = cli.Command{
 		step := ctx.Generic("step")
 
 		metricsName := ctx.String("name")
-		scope := interceptor.ParseScope(metricsName)
+		entity := interceptor.ParseEntity(ctx)
 
 		duration := schema.Duration{
 			Start: start,
@@ -63,10 +57,8 @@ var Command = cli.Command{
 		}
 
 		metricsValues := metrics.Thermodynamic(ctx, schema.MetricsCondition{
-			Name: metricsName,
-			Entity: &schema.Entity{
-				Scope: scope,
-			},
+			Name:   metricsName,
+			Entity: entity,
 		}, duration)
 
 		return display.Display(ctx, &displayable.Displayable{