Skip to content

Commit

Permalink
Add the sub-commands for query sorted metrics/records
Browse files Browse the repository at this point in the history
  • Loading branch information
mrproliu committed Sep 14, 2022
1 parent ec85225 commit fd9b933
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -20,6 +20,7 @@ Release Notes.
- Add the sub-command `profiling ebpf create network` and `profiling ebpf keep network` to create and keep the network eBPF profiling task.(#158)
- Add the sub-command `dependency process` to query the process relation.(#158)
- Support query the metrics of process relation.(#158)
- Add the sub-command `metrics sorted` and `metrics sampled-record` to query the sorted metrics/records.(#163)

0.10.0
------------------
Expand Down
23 changes: 23 additions & 0 deletions assets/graphqls/metrics/SampledRecords.graphql
@@ -0,0 +1,23 @@
# 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 ($condition:TopNCondition!, $duration: Duration!) {
result: readSampledRecords(condition: $condition, duration: $duration) {
name
value
}
}
84 changes: 84 additions & 0 deletions internal/commands/metrics/aggregation/metrics.go
@@ -0,0 +1,84 @@
// 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 (
api "skywalking.apache.org/repo/goapi/query"

"github.com/urfave/cli/v2"

"github.com/apache/skywalking-cli/internal/commands/interceptor"
"github.com/apache/skywalking-cli/internal/flags"
"github.com/apache/skywalking-cli/internal/logger"
"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"
)

var SortedMetrics = &cli.Command{
Name: "sorted",
Usage: "query the top <n> entities sorted by the specified metrics",
ArgsUsage: "<n>",
UsageText: `Query the top <n> entities sorted by the specified metrics.
Examples:
1. Query the top 5 services whose sla are largest:
$ swctl metrics sorted --name service_sla 5
2. Query the top 5 endpoints whose sla is largest:
$ swctl metrics sorted --name endpoint_sla 5
3. Query the top 5 instances of service "boutique::adservice" whose sla are largest:
$ swctl metrics sorted --name service_instance_sla --service-name boutique::adservice 5
`,
Flags: flags.Flags(
flags.DurationFlags,
flags.MetricsFlags,
flags.ServiceFlags,
[]cli.Flag{
&cli.GenericFlag{
Name: "order",
Usage: "the `order` by which the top entities are sorted",
Value: &model.OrderEnumValue{
Enum: api.AllOrder,
Default: api.OrderDes,
Selected: api.OrderDes,
},
},
},
),
Before: interceptor.BeforeChain(
interceptor.DurationInterceptor,
interceptor.ParseService(false),
),
Action: func(ctx *cli.Context) error {
condition, duration, err := buildSortedCondition(ctx, true)
if err != nil {
return err
}

logger.Log.Debugln(condition.Name, condition.Scope, condition.TopN)
metricsValues, err := metrics.SortMetrics(ctx, *condition, *duration)
if err != nil {
return err
}

return display.Display(ctx, &displayable.Displayable{Data: metricsValues})
},
}
78 changes: 78 additions & 0 deletions internal/commands/metrics/aggregation/sampled-record.go
@@ -0,0 +1,78 @@
// 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 (
api "skywalking.apache.org/repo/goapi/query"

"github.com/urfave/cli/v2"

"github.com/apache/skywalking-cli/internal/commands/interceptor"
"github.com/apache/skywalking-cli/internal/flags"
"github.com/apache/skywalking-cli/internal/logger"
"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"
)

var SampledRecords = &cli.Command{
Name: "sampled-record",
Usage: "query the top <n> entities sorted by the specified records",
ArgsUsage: "<n>",
UsageText: `Query the top <n> entities sorted by the specified records.
Examples:
1. Query the top 5 database statements whose execute duration are largest:
$ swctl metrics sampled-record --name top_n_database_statement 5
`,
Flags: flags.Flags(
flags.DurationFlags,
flags.MetricsFlags,
flags.ServiceFlags,
[]cli.Flag{
&cli.GenericFlag{
Name: "order",
Usage: "the `order` by which the top entities are sorted",
Value: &model.OrderEnumValue{
Enum: api.AllOrder,
Default: api.OrderDes,
Selected: api.OrderDes,
},
},
},
),
Before: interceptor.BeforeChain(
interceptor.DurationInterceptor,
interceptor.ParseService(false),
),
Action: func(ctx *cli.Context) error {
condition, duration, err := buildSortedCondition(ctx, false)
if err != nil {
return err
}

logger.Log.Debugln(condition.Name, condition.Scope, condition.TopN)
sampledRecords, err := metrics.SampledRecords(ctx, *condition, *duration)
if err != nil {
return err
}

return display.Display(ctx, &displayable.Displayable{Data: sampledRecords})
},
}
73 changes: 73 additions & 0 deletions internal/commands/metrics/aggregation/sorted-condition.go
@@ -0,0 +1,73 @@
// 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"

"github.com/apache/skywalking-cli/internal/commands/interceptor"
"github.com/apache/skywalking-cli/internal/model"
"github.com/apache/skywalking-cli/pkg/graphql/utils"

"github.com/urfave/cli/v2"

api "skywalking.apache.org/repo/goapi/query"
)

// buildSortedCondition from context of cli, the first argument must be the count of top N
func buildSortedCondition(ctx *cli.Context, parseScope bool) (*api.TopNCondition, *api.Duration, error) {
start := ctx.String("start")
end := ctx.String("end")
step := ctx.Generic("step").(*model.StepEnumValue).Selected

metricsName := ctx.String("name")
var scope *api.Scope
if parseScope {
tmp := utils.ParseScopeInTop(metricsName)
scope = &tmp
}
order := ctx.Generic("order").(*model.OrderEnumValue).Selected
topN := 5
parentServiceID := ctx.String("service-id")
parentService, normal, err := interceptor.ParseServiceID(parentServiceID)
if err != nil {
return nil, nil, err
}

if ctx.NArg() > 0 {
nn, err2 := strconv.Atoi(ctx.Args().First())
if err2 != nil {
return nil, nil, fmt.Errorf("the 1st argument must be a number: %v", err2)
}
topN = nn
}

return &api.TopNCondition{
Name: metricsName,
ParentService: &parentService,
Normal: &normal,
Scope: scope,
TopN: topN,
Order: order,
}, &api.Duration{
Start: start,
End: end,
Step: step,
}, nil
}
42 changes: 3 additions & 39 deletions internal/commands/metrics/aggregation/topn.go
Expand Up @@ -18,9 +18,6 @@
package aggregation

import (
"fmt"
"strconv"

api "skywalking.apache.org/repo/goapi/query"

"github.com/urfave/cli/v2"
Expand All @@ -32,7 +29,6 @@ import (
"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/apache/skywalking-cli/pkg/graphql/utils"
)

var TopN = &cli.Command{
Expand Down Expand Up @@ -72,45 +68,13 @@ $ swctl metrics top --name service_instance_sla --service-name boutique::adservi
interceptor.ParseService(false),
),
Action: func(ctx *cli.Context) error {
start := ctx.String("start")
end := ctx.String("end")
step := ctx.Generic("step").(*model.StepEnumValue).Selected

metricsName := ctx.String("name")
scope := utils.ParseScopeInTop(metricsName)
order := ctx.Generic("order").(*model.OrderEnumValue).Selected
topN := 5
parentServiceID := ctx.String("service-id")
parentService, normal, err := interceptor.ParseServiceID(parentServiceID)
condition, duration, err := buildSortedCondition(ctx, true)
if err != nil {
return err
}

if ctx.NArg() > 0 {
nn, err2 := strconv.Atoi(ctx.Args().First())
if err2 != nil {
return fmt.Errorf("the 1st argument must be a number: %v", err2)
}
topN = nn
}

duration := api.Duration{
Start: start,
End: end,
Step: step,
}

logger.Log.Debugln(metricsName, scope, topN)

metricsValues, err := metrics.SortMetrics(ctx, api.TopNCondition{
Name: metricsName,
ParentService: &parentService,
Normal: &normal,
Scope: &scope,
TopN: topN,
Order: order,
}, duration)

logger.Log.Debugln(condition.Name, condition.Scope, condition.TopN)
metricsValues, err := metrics.SortMetrics(ctx, *condition, *duration)
if err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions internal/commands/metrics/metrics.go
Expand Up @@ -38,6 +38,8 @@ var Command = &cli.Command{
linear.Multiple,
thermodynamic.Command,
aggregation.TopN,
aggregation.SortedMetrics,
aggregation.SampledRecords,
list.Command,
},
}
1 change: 1 addition & 0 deletions pkg/display/display.go
Expand Up @@ -44,6 +44,7 @@ const (
var style = map[string]string{"dashboard global": "graph",
"dashboard global-metrics": "graph",
"metrics top": "table",
"metrics sorted": "table",
"metrics linear": "graph",
"metrics list": "table",
"service list": "table",
Expand Down
12 changes: 12 additions & 0 deletions pkg/graphql/metrics/metrics.go
Expand Up @@ -92,6 +92,18 @@ func SortMetrics(ctx *cli.Context, condition api.TopNCondition, duration api.Dur
return response["result"], err
}

func SampledRecords(ctx *cli.Context, condition api.TopNCondition, duration api.Duration) ([]*api.SelectedRecord, error) {
var response map[string][]*api.SelectedRecord

request := graphql.NewRequest(assets.Read("graphqls/metrics/SampledRecords.graphql"))
request.Var("condition", condition)
request.Var("duration", duration)

err := client.ExecuteQuery(ctx, request, &response)

return response["result"], err
}

func ListMetrics(ctx *cli.Context, regex string) ([]*api.MetricDefinition, error) {
var response map[string][]*api.MetricDefinition
request := graphql.NewRequest(assets.Read("graphqls/metrics/ListMetrics.graphql"))
Expand Down

0 comments on commit fd9b933

Please sign in to comment.