diff --git a/CHANGES.md b/CHANGES.md index 76fce945..23a159fa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -40,6 +40,8 @@ Release Notes. * Add the sub-command `metrics nullable` for query the nullable metrics value. @mrproliu in https://github.com/apache/skywalking-cli/pull/176 * Adapt the sub-command `profiling trace` for adapt the new trace profiling protocol. @mrproliu in https://github.com/apache/skywalking-cli/pull/177 * Add `isEmptyValue` field in metrics related sub-commands. @mrproliu in https://github.com/apache/skywalking-cli/pull/180 +* Add the sub-command `metrics execute` for execute the metrics query. @mrproliu in https://github.com/apache/skywalking-cli/pull/182 +* Add the sub-command `profiling continuous monitoring` for query all continuous profiling monitoring instances. @mrproliu in https://github.com/apache/skywalking-cli/pull/182 0.10.0 ------------------ diff --git a/assets/graphqls/metrics/ExecuteExpression.graphql b/assets/graphqls/metrics/ExecuteExpression.graphql new file mode 100644 index 00000000..a03f378b --- /dev/null +++ b/assets/graphqls/metrics/ExecuteExpression.graphql @@ -0,0 +1,35 @@ +# Licensed to Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Apache Software Foundation (ASF) licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +query ($expression: String!, $entity: Entity!, $duration: Duration!) { + result: execExpression(expression: $expression, entity: $entity, duration: $duration) { + type + results { + metric { + labels { + key value + } + } + values { + id + value + traceID + } + } + error + } +} diff --git a/assets/graphqls/profiling/continuous/QueryContinuousProfilingMonitoringInstances.graphql b/assets/graphqls/profiling/continuous/QueryContinuousProfilingMonitoringInstances.graphql new file mode 100644 index 00000000..2193ad10 --- /dev/null +++ b/assets/graphqls/profiling/continuous/QueryContinuousProfilingMonitoringInstances.graphql @@ -0,0 +1,38 @@ +# Licensed to Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Apache Software Foundation (ASF) licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +query ($serviceId: ID!, $target: ContinuousProfilingTargetType!){ + result: queryContinuousProfilingMonitoringInstances(serviceId: $serviceId, target: $target) { + id + name + attributes { + name + value + } + triggeredCount + lastTriggerTimestamp + + processes { + id + name + detectType + labels + triggeredCount + lastTriggerTimestamp + } + } +} \ No newline at end of file diff --git a/assets/graphqls/profiling/continuous/QueryContinuousProfilingServiceTargets.graphql b/assets/graphqls/profiling/continuous/QueryContinuousProfilingServiceTargets.graphql index ee1def3e..26387d98 100644 --- a/assets/graphqls/profiling/continuous/QueryContinuousProfilingServiceTargets.graphql +++ b/assets/graphqls/profiling/continuous/QueryContinuousProfilingServiceTargets.graphql @@ -26,5 +26,7 @@ query ($serviceId: ID!){ uriList uriRegex } + triggeredCount + lastTriggerTimestamp } } \ No newline at end of file diff --git a/dist/LICENSE b/dist/LICENSE index 157afc6e..5084de4c 100644 --- a/dist/LICENSE +++ b/dist/LICENSE @@ -213,7 +213,7 @@ The text of each license is also included at licenses/license-[project].txt. k8s.io/utils v0.0.0-20210802155522-efc7438f0176 Apache-2.0 sigs.k8s.io/controller-runtime v0.10.0 Apache-2.0 sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0 - skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a Apache-2.0 + skywalking.apache.org/repo/goapi v0.0.0-20230608051849-a29bda277aac Apache-2.0 ======================================================================== BSD-2-Clause licenses @@ -250,7 +250,7 @@ The text of each license is also included at licenses/license-[project].txt. golang.org/x/term v0.5.0 BSD-3-Clause golang.org/x/text v0.7.0 BSD-3-Clause golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac BSD-3-Clause - google.golang.org/protobuf v1.29.0 BSD-3-Clause + google.golang.org/protobuf v1.30.0 BSD-3-Clause gopkg.in/inf.v0 v0.9.1 BSD-3-Clause ======================================================================== diff --git a/go.mod b/go.mod index edc77238..442ba989 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 k8s.io/apimachinery v0.22.1 sigs.k8s.io/controller-runtime v0.10.0 - skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a + skywalking.apache.org/repo/goapi v0.0.0-20230608051849-a29bda277aac ) require ( @@ -77,7 +77,7 @@ require ( gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 // indirect - google.golang.org/protobuf v1.29.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index a97d4e5c..d1777bba 100644 --- a/go.sum +++ b/go.sum @@ -789,8 +789,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0= -google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -864,5 +864,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3 sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a h1:m8DTnaSEOEnPXRWmA6g7isbdqw7WPZP6SnaEHz1Sx7s= -skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a/go.mod h1:LcZMcxDjdJPn5yetydFnxe0l7rmiv8lvHEnzRbsey14= +skywalking.apache.org/repo/goapi v0.0.0-20230608051849-a29bda277aac h1:8Ra0KXu0lNlEKCRvsNbBXYEFpMx5Tmfm0krekUM9FNE= +skywalking.apache.org/repo/goapi v0.0.0-20230608051849-a29bda277aac/go.mod h1:bW4dg0GUN4rMCMS8DLlaY3ZiKUAJ1fQYKoZ91Bl0kTk= diff --git a/internal/commands/metrics/expression/exec.go b/internal/commands/metrics/expression/exec.go new file mode 100644 index 00000000..25c414e2 --- /dev/null +++ b/internal/commands/metrics/expression/exec.go @@ -0,0 +1,87 @@ +// 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 expression + +import ( + "github.com/apache/skywalking-cli/internal/commands/interceptor" + "github.com/apache/skywalking-cli/internal/flags" + "github.com/apache/skywalking-cli/internal/model" + "github.com/apache/skywalking-cli/pkg/display" + "github.com/apache/skywalking-cli/pkg/display/displayable" + "github.com/apache/skywalking-cli/pkg/graphql/metrics" + + "github.com/urfave/cli/v2" + + api "skywalking.apache.org/repo/goapi/query" +) + +const expressionParameterName = "expression" + +var ExecCommand = &cli.Command{ + Name: "execute", + Aliases: []string{"exec"}, + Usage: `Execute a metrics expression.`, + UsageText: `Execute a metrics expression. + +Examples: +1. Execute the given expression of service "business-zone::projectB" +$ swctl metrics execute --expression="service_resp_time" --service-name business-zone::projectB`, + Flags: flags.Flags( + flags.DurationFlags, + flags.InstanceRelationFlags, + flags.EndpointRelationFlags, + flags.ProcessRelationFlags, + []cli.Flag{ + &cli.StringFlag{ + Name: expressionParameterName, + Usage: "metrics expression", + Required: true, + }, + }, + ), + Before: interceptor.BeforeChain( + interceptor.DurationInterceptor, + interceptor.ParseEndpointRelation(false), + interceptor.ParseInstanceRelation(false), + interceptor.ParseProcessRelation(false), + ), + Action: func(ctx *cli.Context) error { + end := ctx.String("end") + start := ctx.String("start") + step := ctx.Generic("step") + + expression := ctx.String(expressionParameterName) + entity, err := interceptor.ParseEntity(ctx) + if err != nil { + return err + } + + duration := api.Duration{ + Start: start, + End: end, + Step: step.(*model.StepEnumValue).Selected, + } + + result, err := metrics.Execute(ctx, expression, entity, duration) + if err != nil { + return err + } + + return display.Display(ctx, &displayable.Displayable{Data: result}) + }, +} diff --git a/internal/commands/metrics/metrics.go b/internal/commands/metrics/metrics.go index 51417291..25fe502c 100644 --- a/internal/commands/metrics/metrics.go +++ b/internal/commands/metrics/metrics.go @@ -25,6 +25,7 @@ import ( "github.com/apache/skywalking-cli/internal/commands/metrics/thermodynamic" + "github.com/apache/skywalking-cli/internal/commands/metrics/expression" "github.com/apache/skywalking-cli/internal/commands/metrics/linear" "github.com/apache/skywalking-cli/internal/commands/metrics/single" ) @@ -42,5 +43,6 @@ var Command = &cli.Command{ aggregation.SortedMetrics, aggregation.SampledRecords, list.Command, + expression.ExecCommand, }, } diff --git a/internal/commands/profiling/continuous/continuous.go b/internal/commands/profiling/continuous/continuous.go index 7e7d9692..210bd7a4 100644 --- a/internal/commands/profiling/continuous/continuous.go +++ b/internal/commands/profiling/continuous/continuous.go @@ -25,5 +25,6 @@ var Command = &cli.Command{ Subcommands: []*cli.Command{ SetPolicyCommand, ListCommand, + Monitoring, }, } diff --git a/internal/commands/profiling/continuous/monitoring.go b/internal/commands/profiling/continuous/monitoring.go new file mode 100644 index 00000000..d8d81262 --- /dev/null +++ b/internal/commands/profiling/continuous/monitoring.go @@ -0,0 +1,81 @@ +// 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 continuous + +import ( + "fmt" + "strings" + + "github.com/apache/skywalking-cli/internal/commands/interceptor" + "github.com/apache/skywalking-cli/internal/flags" + "github.com/apache/skywalking-cli/pkg/display" + "github.com/apache/skywalking-cli/pkg/display/displayable" + "github.com/apache/skywalking-cli/pkg/graphql/profiling" + + "github.com/urfave/cli/v2" + + api "skywalking.apache.org/repo/goapi/query" +) + +var Monitoring = &cli.Command{ + Name: "monitoring", + Aliases: []string{"monitor"}, + Usage: `query all continuous profiling monitoring instances through service and policy target`, + UsageText: `Query all continuous profiling monitoring instances through service and policy target. + +Example: +1. Query all continuous profiling monitoring instances through service "business-zone::projectC" and policy target "ON_CPU" +$ swctl profiling continuous monitoring --service-name=business-zone::projectC --target=ON_CPU +`, + Flags: flags.Flags( + flags.ServiceFlags, + []cli.Flag{ + &cli.StringFlag{ + Name: "target", + Usage: "policy target type", + Required: true, + }, + }, + ), + Before: interceptor.BeforeChain( + interceptor.ParseService(true), + ), + Action: func(ctx *cli.Context) error { + serviceID := ctx.String("service-id") + targetString := ctx.String("target") + targetHasSet := false + var target api.ContinuousProfilingTargetType + for _, enum := range api.AllContinuousProfilingTargetType { + if strings.EqualFold(enum.String(), targetString) { + target = enum + targetHasSet = true + break + } + } + if !targetHasSet { + return fmt.Errorf("unknown target type: %s", targetString) + } + + instances, err := profiling.QueryContinuousProfilingMonitoringInstances(ctx, serviceID, target) + if err != nil { + return err + } + + return display.Display(ctx, &displayable.Displayable{Data: instances}) + }, +} diff --git a/pkg/graphql/metrics/metrics.go b/pkg/graphql/metrics/metrics.go index 44f45c73..9fcae90b 100644 --- a/pkg/graphql/metrics/metrics.go +++ b/pkg/graphql/metrics/metrics.go @@ -138,3 +138,16 @@ func ListMetrics(ctx *cli.Context, regex string) ([]*api.MetricDefinition, error return response["result"], err } + +func Execute(ctx *cli.Context, expression string, entity *api.Entity, duration api.Duration) (api.ExpressionResult, error) { + var response map[string]api.ExpressionResult + + request := graphql.NewRequest(assets.Read("graphqls/metrics/ExecuteExpression.graphql")) + request.Var("expression", expression) + request.Var("entity", *entity) + request.Var("duration", duration) + + err := client.ExecuteQuery(ctx, request, &response) + + return response["result"], err +} diff --git a/pkg/graphql/profiling/continuous.go b/pkg/graphql/profiling/continuous.go index 96b1785c..7d590ead 100644 --- a/pkg/graphql/profiling/continuous.go +++ b/pkg/graphql/profiling/continuous.go @@ -49,3 +49,16 @@ func QueryContinuousProfilingServiceTargets(ctx *cli.Context, serviceID string) return response["result"], err } + +func QueryContinuousProfilingMonitoringInstances(ctx *cli.Context, serviceID string, + target api.ContinuousProfilingTargetType) ([]api.ContinuousProfilingMonitoringInstance, error) { + var response map[string][]api.ContinuousProfilingMonitoringInstance + + request := graphql.NewRequest(assets.Read("graphqls/profiling/continuous/QueryContinuousProfilingMonitoringInstances.graphql")) + request.Var("serviceId", serviceID) + request.Var("target", target) + + err := client.ExecuteQuery(ctx, request, &response) + + return response["result"], err +}