This is an automated email from the ASF dual-hosted git repository.
wu-sheng 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 85e5afd Add `admin inspect values` to read foreign metric values
(#232)
85e5afd is described below
commit 85e5afdb3d55c6e5af66a472c3fe8ac024d11690
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Wed Jun 24 21:39:21 2026 +0800
Add `admin inspect values` to read foreign metric values (#232)
The new sub-command POSTs to the OAP admin API `POST /inspect/values` to
evaluate an MQE expression over metric(s) the target OAP does not define
locally (foreign metrics), supplying each metric's {valueColumn, valueType}.
The entity scope is inferred from which name flag is set (--service-name /
--service-instance-name / --endpoint-name). Returns the native MQE result.
---
CHANGES.md | 1 +
internal/commands/admin/inspect/inspect.go | 95 ++++++++++++++++++++++++++++++
pkg/admin/inspect/inspect.go | 83 ++++++++++++++++++++++++++
3 files changed, 179 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index 6de747b..a401f8a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,6 +17,7 @@ Release Notes.
* Remove the oldest `queryTraceFromColdStage` query call in the `trace list`
command by @mrproliu in https://github.com/apache/skywalking-cli/pull/225
* Add the sub-command `profiling pprof` for pprof query API by @JophieQu in
https://github.com/apache/skywalking-cli/pull/226
* Add the `admin` command group for the OAP admin-server REST host (default
port `17128`), with a new global `--admin-url` flag (derived from `--base-url`
when unset). Covers every admin feature module: `admin preflight`; `admin
cluster nodes`, `admin config dump|ttl`, `admin alarm rules|rule` (status);
`admin inspect metrics|entities` (inspect); `admin ui-template
list|get|create|update|disable` (ui-management); `admin runtime-rule
list|bundled|get|add|inactivate|delete|dump` (runtime-r [...]
+* Add the sub-command `admin inspect values` to read the VALUES of metric(s)
an OAP does not define locally (foreign metrics) by supplying their
`{valueColumn, valueType}` metadata, via the new `POST /inspect/values` admin
API; returns the native MQE result.
### Bug Fixes
diff --git a/internal/commands/admin/inspect/inspect.go
b/internal/commands/admin/inspect/inspect.go
index 385807a..df9f1c3 100644
--- a/internal/commands/admin/inspect/inspect.go
+++ b/internal/commands/admin/inspect/inspect.go
@@ -19,6 +19,7 @@ package inspect
import (
"fmt"
+ "strings"
"github.com/urfave/cli/v2"
@@ -37,6 +38,7 @@ var Command = &cli.Command{
Subcommands: []*cli.Command{
metricsCommand,
entitiesCommand,
+ valuesCommand,
},
}
@@ -144,3 +146,96 @@ $ swctl admin inspect entities --metric meter_foo
--value-column value --value-t
return display.Display(ctx.Context,
&displayable.Displayable{Data: entities})
},
}
+
+var valuesCommand = &cli.Command{
+ Name: "values",
+ Usage: "Read the VALUES of metric(s) persisted by another OAP (POST
/inspect/values)",
+ UsageText: `Evaluate an MQE expression over metric(s) the target OAP
does not define locally, by
+supplying each foreign metric's metadata. Returns the native MQE result (the
same shape the UI
+renders for catalog metrics). The entity scope is inferred from which name
flags are set.
+
+Example — read a foreign service metric's value series:
+$ swctl admin inspect values --expression meter_foo --service-name my-svc \
+ --foreign-metric meter_foo,value,LONG`,
+ Flags: flags.Flags(
+ flags.DurationFlags,
+ []cli.Flag{
+ &cli.StringFlag{
+ Name: "expression",
+ Usage: "the MQE `expression` to evaluate",
+ Required: true,
+ },
+ &cli.StringFlag{
+ Name: "service-name",
+ Usage: "the `service` entity to query",
+ Required: true,
+ },
+ &cli.StringFlag{
+ Name: "service-instance-name",
+ Usage: "the `instance` to query (selects
ServiceInstance scope)",
+ },
+ &cli.StringFlag{
+ Name: "endpoint-name",
+ Usage: "the `endpoint` to query (selects
Endpoint scope)",
+ },
+ &cli.BoolFlag{
+ Name: "normal",
+ Usage: "whether the service is normal
(agent-reported)",
+ Value: true,
+ },
+ &cli.StringSliceFlag{
+ Name: "foreign-metric",
+ Usage: "metadata for one foreign metric as
`name,valueColumn,valueType` " +
+ "(repeatable — supply one per foreign
metric in the expression)",
+ Required: true,
+ },
+ },
+ ),
+ Before: interceptor.BeforeChain(
+ interceptor.DurationInterceptor,
+ ),
+ Action: func(ctx *cli.Context) error {
+ step := ctx.Generic("step").(*model.StepEnumValue).Selected
+
+ entity := inspect.QueryEntity{
+ ServiceName: ctx.String("service-name"),
+ Normal: ctx.Bool("normal"),
+ }
+ switch {
+ case ctx.String("endpoint-name") != "":
+ entity.Scope = "Endpoint"
+ entity.EndpointName = ctx.String("endpoint-name")
+ case ctx.String("service-instance-name") != "":
+ entity.Scope = "ServiceInstance"
+ entity.ServiceInstanceName =
ctx.String("service-instance-name")
+ default:
+ entity.Scope = "Service"
+ }
+
+ var foreign []inspect.ForeignMetricInput
+ for _, fm := range ctx.StringSlice("foreign-metric") {
+ parts := strings.Split(fm, ",")
+ if len(parts) != 3 {
+ return fmt.Errorf("--foreign-metric must be
name,valueColumn,valueType (got %q)", fm)
+ }
+ foreign = append(foreign, inspect.ForeignMetricInput{
+ Name: parts[0],
+ ValueColumn: parts[1],
+ ValueType: parts[2],
+ })
+ }
+
+ result, err := inspect.ListValues(ctx.Context,
&inspect.ValuesOptions{
+ Expression: ctx.String("expression"),
+ Entity: entity,
+ Start: ctx.String("start"),
+ End: ctx.String("end"),
+ Step: string(step),
+ ForeignMetrics: foreign,
+ })
+ if err != nil {
+ return preflight.Explain(ctx.Context, err,
preflight.ModuleInspect, "SW_INSPECT")
+ }
+ return display.Display(ctx.Context,
&displayable.Displayable{Data: result})
+ },
+}
diff --git a/pkg/admin/inspect/inspect.go b/pkg/admin/inspect/inspect.go
index ef3ee20..8d80295 100644
--- a/pkg/admin/inspect/inspect.go
+++ b/pkg/admin/inspect/inspect.go
@@ -23,6 +23,7 @@ package inspect
import (
"context"
+ "net/http"
"net/url"
"strconv"
@@ -132,3 +133,85 @@ func ListEntities(ctx context.Context, opts
*EntitiesOptions) (*Entities, error)
err := client.GetJSON(ctx, "/inspect/entities", query, &out)
return &out, err
}
+
+// QueryEntity is the MQE query entity for a value query; its scope binds
every foreign metric.
+type QueryEntity struct {
+ Scope string `json:"scope,omitempty"`
+ ServiceName string `json:"serviceName,omitempty"`
+ Normal bool `json:"normal"`
+ ServiceInstanceName string `json:"serviceInstanceName,omitempty"`
+ EndpointName string `json:"endpointName,omitempty"`
+}
+
+// ForeignMetricInput is caller-supplied metadata for one metric the target
OAP does not define.
+type ForeignMetricInput struct {
+ Name string `json:"name"`
+ ValueColumn string `json:"valueColumn"`
+ ValueType string `json:"valueType"`
+}
+
+// ValuesOptions holds the parameters of POST /inspect/values.
+type ValuesOptions struct {
+ Expression string
+ Entity QueryEntity
+ Start string
+ End string
+ Step string
+ ForeignMetrics []ForeignMetricInput
+}
+
+// ExpressionResult is the native MQE result returned by POST /inspect/values,
mirroring the
+// GraphQL execExpression shape.
+type ExpressionResult struct {
+ Type string `json:"type"`
+ Error string `json:"error,omitempty"`
+ Results []MQEValues `json:"results"`
+}
+
+// MQEValues is one (optionally labeled) series of an ExpressionResult.
+type MQEValues struct {
+ Metric MQEMetric `json:"metric"`
+ Values []MQEValue `json:"values"`
+}
+
+// MQEMetric carries the label set of an MQEValues series.
+type MQEMetric struct {
+ Labels []KeyValue `json:"labels"`
+}
+
+// MQEValue is one time-bucket point.
+type MQEValue struct {
+ ID string `json:"id"`
+ Value string `json:"value"`
+}
+
+// KeyValue is a metric label.
+type KeyValue struct {
+ Key string `json:"key"`
+ Value string `json:"value"`
+}
+
+type valuesRequest struct {
+ Expression string `json:"expression"`
+ Entity QueryEntity `json:"entity"`
+ Start string `json:"start"`
+ End string `json:"end"`
+ Step string `json:"step"`
+ ForeignMetrics []ForeignMetricInput `json:"foreignMetrics"`
+}
+
+// ListValues evaluates an MQE expression over foreign metric(s) — metrics the
target OAP does not
+// define locally — by supplying their metadata (POST /inspect/values).
Returns the native MQE result.
+func ListValues(ctx context.Context, opts *ValuesOptions) (*ExpressionResult,
error) {
+ body := valuesRequest{
+ Expression: opts.Expression,
+ Entity: opts.Entity,
+ Start: opts.Start,
+ End: opts.End,
+ Step: opts.Step,
+ ForeignMetrics: opts.ForeignMetrics,
+ }
+ var out ExpressionResult
+ err := client.SendJSON(ctx, http.MethodPost, "/inspect/values", nil,
body, &out)
+ return &out, err
+}