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/07 10:21:15 UTC

[skywalking-cli] branch feature/thermodynamic created (now dd20523)

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

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


      at dd20523  [Feature] Support top N entities and thermodynamic metrics

This branch includes the following new commits:

     new dd20523  [Feature] Support top N entities and thermodynamic metrics

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.



[skywalking-cli] 01/01: [Feature] Support top N entities and thermodynamic metrics

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

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

commit dd20523764aac52ba80692a722ba5ce373b6c3cc
Author: kezhenxu94 <ke...@163.com>
AuthorDate: Sat Mar 7 18:20:56 2020 +0800

    [Feature] Support top N entities and thermodynamic metrics
    
    ### Motivation
    
    Support top N entities and thermodynamic metrics commands
    
    ### Result
    
    - Commands `metrics top n` and `thermodynamic` are added
    
    - Closes https://github.com/apache/skywalking/issues/3898
    
    - Closes https://github.com/apache/skywalking/issues/3897
---
 .golangci.yml                                   |   4 +-
 README.md                                       |  49 ++++++++
 commands/metrics/aggregation/topn.go            | 112 +++++++++++++++++
 commands/metrics/metrics.go                     |   6 +
 commands/metrics/thermodynamic/thermodynamic.go |  67 ++++++++++
 commands/model/{step.go => order.go}            |  28 ++---
 commands/model/step.go                          |   2 +-
 graphql/aggregation/aggregation.go              | 155 ++++++++++++++++++++++++
 graphql/metrics/metrics.go                      |  18 +++
 9 files changed, 424 insertions(+), 17 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index 26b94e2..433731a 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -24,7 +24,7 @@ linters-settings:
   maligned:
     suggest-new: true
   dupl:
-    threshold: 100
+    threshold: 200
   goconst:
     min-len: 2
     min-occurrences: 2
@@ -93,4 +93,4 @@ linters:
 service:
   golangci-lint-version: 1.20.x
   prepare:
-    - echo "here I can run custom commands, but no preparation needed for this repo"
\ No newline at end of file
+    - echo "here I can run custom commands, but no preparation needed for this repo"
diff --git a/README.md b/README.md
index 0467d09..b13ee05 100644
--- a/README.md
+++ b/README.md
@@ -197,6 +197,22 @@ This section covers all the available commands in SkyWalking CLI and their usage
 
 </details>
 
+#### `metrics top <n>`
+
+<details>
+
+<summary>metrics top 3 [--start=start-time] [--end=end-time] --name endpoint_sla [--service-id 3]</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 `service_sla`, etc. |
+| `--service-id` | service ID that are required by the metric type, such as service IDs for `service_sla` |
+| `--start` | See [Common options](#common-options) | See [Common options](#common-options) |
+| `--end` | See [Common options](#common-options) | See [Common options](#common-options) |
+| arguments | the first argument is the number of top entities | `3` |
+
+</details>
+
 # Use Cases
 
 <details>
@@ -395,6 +411,39 @@ $ ./bin/swctl-latest-darwin-amd64 --display=graph --debug metrics multiple-linea
 
 <details>
 
+<summary>Query the top 5 services whose sla is largest</summary>
+
+```shell
+$ ./bin/swctl-latest-darwin-amd64 metrics top 5 --name service_sla        
+[{"name":"projectB","id":"2","value":10000},{"name":"projectC","id":"3","value":10000},{"name":"projectA","id":"4","value":10000},{"name":"projectD","id":"5","value":10000}]
+```
+
+</details>
+
+<details>
+
+<summary>Query the top 5 instances whose sla is largest, of service (id = 3)</summary>
+
+```shell
+$ ./bin/swctl-latest-darwin-amd64 metrics top 5 --name service_instance_sla --service-id 3        
+[{"name":"projectC-pid:30335@skywalking-server-0002","id":"13","value":10000},{"name":"projectC-pid:22037@skywalking-server-0001","id":"2","value":10000}]
+```
+
+</details>
+
+<details>
+
+<summary>Query the top 5 endpoints whose sla is largest, of service (id = 3)</summary>
+
+```shell
+$ ./bin/swctl-latest-darwin-amd64 metrics top 5 --name endpoint_sla --service-id 3        
+[{"name":"/projectC/{value}","id":"4","value":10000}]
+```
+
+</details>
+
+<details>
+
 <summary>Automatically convert to server side timezone</summary>
 
 if your backend nodes are deployed in docker and the timezone is UTC, you may not want to convert your timezone to UTC every time you type a command, `--timezone` comes to your rescue.
diff --git a/commands/metrics/aggregation/topn.go b/commands/metrics/aggregation/topn.go
new file mode 100644
index 0000000..77876f6
--- /dev/null
+++ b/commands/metrics/aggregation/topn.go
@@ -0,0 +1,112 @@
+// 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 aggregation
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/apache/skywalking-cli/commands/interceptor"
+
+	"github.com/urfave/cli"
+
+	"github.com/apache/skywalking-cli/commands/flags"
+	"github.com/apache/skywalking-cli/commands/model"
+	"github.com/apache/skywalking-cli/display"
+	"github.com/apache/skywalking-cli/graphql/aggregation"
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
+
+var TopN = cli.Command{
+	Name:      "top",
+	Usage:     "query top `n` entities",
+	ArgsUsage: "<n>",
+	Flags: flags.Flags(
+		flags.DurationFlags,
+		[]cli.Flag{
+			cli.StringFlag{
+				Name:     "name",
+				Usage:    "`metrics name`, which should be defined in OAL script",
+				Required: true,
+			},
+			cli.GenericFlag{
+				Name:  "order",
+				Usage: "the `order` by which the top entities are sorted",
+				Value: &model.OrderEnumValue{
+					Enum:     schema.AllOrder,
+					Default:  schema.OrderDes,
+					Selected: schema.OrderDes,
+				},
+			},
+			cli.StringFlag{
+				Name:     "service-id",
+				Usage:    "the `service id` whose instances/endpoints are to be fetch, if applicable",
+				Required: false,
+			},
+		},
+	),
+	Before: interceptor.BeforeChain([]cli.BeforeFunc{
+		interceptor.TimezoneInterceptor,
+		interceptor.DurationInterceptor,
+	}),
+	Action: func(ctx *cli.Context) error {
+		name := ctx.String("name")
+		start := ctx.String("start")
+		end := ctx.String("end")
+		step := ctx.Generic("step").(*model.StepEnumValue).Selected
+		order := ctx.Generic("order").(*model.OrderEnumValue).Selected
+		serviceID := ctx.String("service-id")
+
+		topN := 5
+
+		if ctx.NArg() > 0 {
+			nn, err := strconv.Atoi(ctx.Args().First())
+			if err != nil {
+				return fmt.Errorf("the 1st argument must be a number")
+			}
+			topN = nn
+		}
+
+		duration := schema.Duration{
+			Start: start,
+			End:   end,
+			Step:  step,
+		}
+
+		var metricsValues []schema.TopNEntity
+
+		if strings.HasPrefix(name, "service_instance") {
+			if serviceID == "" {
+				metricsValues = aggregation.AllServiceInstanceTopN(ctx, name, topN, duration, order)
+			} else {
+				metricsValues = aggregation.ServiceInstanceTopN(ctx, serviceID, name, topN, duration, order)
+			}
+		} else if strings.HasPrefix(name, "endpoint_") {
+			if serviceID == "" {
+				metricsValues = aggregation.AllEndpointTopN(ctx, name, topN, duration, order)
+			} else {
+				metricsValues = aggregation.EndpointTopN(ctx, serviceID, name, topN, duration, order)
+			}
+		} else if strings.HasPrefix(name, "service_") {
+			metricsValues = aggregation.ServiceTopN(ctx, name, topN, duration, order)
+		}
+
+		return display.Display(ctx, metricsValues)
+	},
+}
diff --git a/commands/metrics/metrics.go b/commands/metrics/metrics.go
index 8f941b9..d1b4a8f 100644
--- a/commands/metrics/metrics.go
+++ b/commands/metrics/metrics.go
@@ -20,6 +20,10 @@ package metrics
 import (
 	"github.com/urfave/cli"
 
+	"github.com/apache/skywalking-cli/commands/metrics/aggregation"
+
+	"github.com/apache/skywalking-cli/commands/metrics/thermodynamic"
+
 	"github.com/apache/skywalking-cli/commands/metrics/linear"
 	"github.com/apache/skywalking-cli/commands/metrics/single"
 )
@@ -31,5 +35,7 @@ var Command = cli.Command{
 		single.Command,
 		linear.Single,
 		linear.Multiple,
+		thermodynamic.Command,
+		aggregation.TopN,
 	},
 }
diff --git a/commands/metrics/thermodynamic/thermodynamic.go b/commands/metrics/thermodynamic/thermodynamic.go
new file mode 100644
index 0000000..5eefb3a
--- /dev/null
+++ b/commands/metrics/thermodynamic/thermodynamic.go
@@ -0,0 +1,67 @@
+// 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 thermodynamic
+
+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/metrics"
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
+
+var Command = cli.Command{
+	Name:      "thermodynamic",
+	ShortName: "td",
+	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",
+				Required: true,
+			},
+		},
+	),
+	Before: interceptor.BeforeChain([]cli.BeforeFunc{
+		interceptor.TimezoneInterceptor,
+		interceptor.DurationInterceptor,
+	}),
+	Action: func(ctx *cli.Context) error {
+		end := ctx.String("end")
+		start := ctx.String("start")
+		step := ctx.Generic("step")
+		metricsName := ctx.String("name")
+
+		duration := schema.Duration{
+			Start: start,
+			End:   end,
+			Step:  step.(*model.StepEnumValue).Selected,
+		}
+
+		metricsValues := metrics.Thermodynamic(ctx, schema.MetricCondition{
+			Name: metricsName,
+		}, duration)
+
+		return display.Display(ctx, metricsValues)
+	},
+}
diff --git a/commands/model/step.go b/commands/model/order.go
similarity index 63%
copy from commands/model/step.go
copy to commands/model/order.go
index 719799d..77a20ba 100644
--- a/commands/model/step.go
+++ b/commands/model/order.go
@@ -24,29 +24,29 @@ import (
 	"github.com/apache/skywalking-cli/graphql/schema"
 )
 
-// StepEnumValue defines the values domain of --step option
-type StepEnumValue struct {
-	Enum     []schema.Step
-	Default  schema.Step
-	Selected schema.Step
+// OrderEnumValue defines the values domain of --order option
+type OrderEnumValue struct {
+	Enum     []schema.Order
+	Default  schema.Order
+	Selected schema.Order
 }
 
-// Set the --step value, from raw string to StepEnumValue
-func (s *StepEnumValue) Set(value string) error {
+// Set the --order value, from raw string to OrderEnumValue
+func (s *OrderEnumValue) Set(value string) error {
 	for _, enum := range s.Enum {
-		if enum.String() == value {
+		if strings.EqualFold(enum.String(), value) {
 			s.Selected = enum
 			return nil
 		}
 	}
-	steps := make([]string, len(schema.AllStep))
-	for i, step := range schema.AllStep {
-		steps[i] = step.String()
+	orders := make([]string, len(schema.AllOrder))
+	for i, order := range schema.AllOrder {
+		orders[i] = order.String()
 	}
-	return fmt.Errorf("allowed steps are %s", strings.Join(steps, ", "))
+	return fmt.Errorf("allowed orders are %s", strings.Join(orders, ", "))
 }
 
-// String representation of the step
-func (s StepEnumValue) String() string {
+// String representation of the order
+func (s OrderEnumValue) String() string {
 	return s.Selected.String()
 }
diff --git a/commands/model/step.go b/commands/model/step.go
index 719799d..8f363be 100644
--- a/commands/model/step.go
+++ b/commands/model/step.go
@@ -34,7 +34,7 @@ type StepEnumValue struct {
 // Set the --step value, from raw string to StepEnumValue
 func (s *StepEnumValue) Set(value string) error {
 	for _, enum := range s.Enum {
-		if enum.String() == value {
+		if strings.EqualFold(enum.String(), value) {
 			s.Selected = enum
 			return nil
 		}
diff --git a/graphql/aggregation/aggregation.go b/graphql/aggregation/aggregation.go
new file mode 100644
index 0000000..fc11392
--- /dev/null
+++ b/graphql/aggregation/aggregation.go
@@ -0,0 +1,155 @@
+// 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 aggregation
+
+import (
+	"github.com/machinebox/graphql"
+	"github.com/urfave/cli"
+
+	"github.com/apache/skywalking-cli/graphql/client"
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
+
+func ServiceTopN(ctx *cli.Context, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
+	var response map[string][]schema.TopNEntity
+
+	request := graphql.NewRequest(`
+		query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+			result: getServiceTopN(
+				duration: $duration,
+				name: $name,
+				topN: $topN,
+				order: $order
+			) {
+				id name value
+			}
+		}
+	`)
+	request.Var("name", name)
+	request.Var("topN", topN)
+	request.Var("duration", duration)
+	request.Var("order", order)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["result"]
+}
+
+func AllServiceInstanceTopN(ctx *cli.Context, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
+	var response map[string][]schema.TopNEntity
+
+	request := graphql.NewRequest(`
+		query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+			result: getAllServiceInstanceTopN(
+				duration: $duration,
+				name: $name,
+				topN: $topN,
+				order: $order
+			) {
+				id name value
+			}
+		}
+	`)
+	request.Var("name", name)
+	request.Var("topN", topN)
+	request.Var("duration", duration)
+	request.Var("order", order)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["result"]
+}
+
+func ServiceInstanceTopN(ctx *cli.Context, serviceID, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
+	var response map[string][]schema.TopNEntity
+
+	request := graphql.NewRequest(`
+		query ($serviceId: ID!, $name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+			result: getServiceInstanceTopN(
+				serviceId: $serviceId,
+				duration: $duration,
+				name: $name,
+				topN: $topN,
+				order: $order
+			) {
+				id name value
+			}
+		}
+	`)
+	request.Var("serviceId", serviceID)
+	request.Var("name", name)
+	request.Var("topN", topN)
+	request.Var("duration", duration)
+	request.Var("order", order)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["result"]
+}
+
+func AllEndpointTopN(ctx *cli.Context, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
+	var response map[string][]schema.TopNEntity
+
+	request := graphql.NewRequest(`
+		query ($name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+			result: getAllEndpointTopN(
+				duration: $duration,
+				name: $name,
+				topN: $topN,
+				order: $order
+			) {
+				id name value
+			}
+		}
+	`)
+	request.Var("name", name)
+	request.Var("topN", topN)
+	request.Var("duration", duration)
+	request.Var("order", order)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["result"]
+}
+
+func EndpointTopN(ctx *cli.Context, serviceID, name string, topN int, duration schema.Duration, order schema.Order) []schema.TopNEntity {
+	var response map[string][]schema.TopNEntity
+
+	request := graphql.NewRequest(`
+		query ($serviceId: ID!, $name: String!, $topN: Int!, $duration: Duration!, $order: Order!) {
+			result: getEndpointTopN(
+				serviceId: $serviceId,
+				duration: $duration,
+				name: $name,
+				topN: $topN,
+				order: $order
+			) {
+				id name value
+			}
+		}
+	`)
+	request.Var("serviceId", serviceID)
+	request.Var("name", name)
+	request.Var("topN", topN)
+	request.Var("duration", duration)
+	request.Var("order", order)
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["result"]
+}
diff --git a/graphql/metrics/metrics.go b/graphql/metrics/metrics.go
index c086e51..68cf36d 100644
--- a/graphql/metrics/metrics.go
+++ b/graphql/metrics/metrics.go
@@ -80,3 +80,21 @@ func MultipleLinearIntValues(ctx *cli.Context, condition schema.MetricCondition,
 
 	return response["metrics"]
 }
+
+func Thermodynamic(ctx *cli.Context, condition schema.MetricCondition, duration schema.Duration) schema.Thermodynamic {
+	request := graphql.NewRequest(`
+		query ($metric: MetricCondition!, $duration: Duration!) {
+			metrics: getThermodynamic(metric: $metric, duration: $duration) {
+				nodes responseTimeStep: axisYStep
+			}
+		}
+	`)
+	request.Var("metric", condition)
+	request.Var("duration", duration)
+
+	var response map[string]schema.Thermodynamic
+
+	client.ExecuteQueryOrFail(ctx, request, &response)
+
+	return response["metrics"]
+}