This is an automated email from the ASF dual-hosted git repository.
wankai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git
The following commit(s) were added to refs/heads/master by this push:
new a8b0d18943 Remove group and support labels in the meter
histogram-percentile function. (#12101)
a8b0d18943 is described below
commit a8b0d18943514164d231ac5a4e305b1439445d8f
Author: Wan Kai <[email protected]>
AuthorDate: Mon Apr 15 15:22:36 2024 +0800
Remove group and support labels in the meter histogram-percentile function.
(#12101)
---
.../skywalking/oap/meter/analyzer/Analyzer.java | 14 +-
.../oap/meter/analyzer/dsl/AnalyzerTest.java | 14 +-
.../analysis/meter/function/BucketedValues.java | 6 +-
.../meter/function/PercentileFunction.java | 318 ---------------------
.../avg/AvgHistogramPercentileFunction.java | 26 +-
.../sum/SumHistogramPercentileFunction.java | 26 +-
.../meter/function/PercentileFunctionTest.java | 230 ---------------
.../function/avg/AvgHistogramFunctionTest.java | 3 -
.../avg/AvgHistogramPercentileFunctionTest.java | 12 +-
.../sum/SumHistogramPercentileFunctionTest.java | 14 +-
.../expected/metrics-has-latency-value-label.yml | 147 +++++++++-
.../expected/metrics-has-value-percentile.yml} | 21 +-
test/e2e-v2/cases/so11y/so11y-cases.yaml | 2 +
13 files changed, 206 insertions(+), 627 deletions(-)
diff --git
a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/Analyzer.java
b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/Analyzer.java
index 82af27879f..debc3d37b0 100644
---
a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/Analyzer.java
+++
b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/Analyzer.java
@@ -27,7 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@@ -159,9 +158,9 @@ public class Analyzer {
break;
case histogram:
case histogramPercentile:
- Stream.of(ss).map(s ->
Tuple.of(composeGroup(s.getLabels(), k -> !Objects.equals("le", k)), s))
+ Stream.of(ss).map(s ->
Tuple.of(getDataLabels(s.getLabels(), k -> !Objects.equals("le", k)), s))
.collect(groupingBy(Tuple2::_1, mapping(Tuple2::_2,
toList())))
- .forEach((group, subSs) -> {
+ .forEach((dataLabel, subSs) -> {
if (subSs.size() < 1) {
return;
}
@@ -178,7 +177,7 @@ public class Analyzer {
vv[i] = getValue(s);
}
BucketedValues bv = new BucketedValues(bb, vv);
- bv.setGroup(group);
+ bv.setLabels(dataLabel);
long time = subSs.get(0).getTimestamp();
if (metricType == MetricType.histogram) {
AcceptableValue<BucketedValues> v =
meterSystem.buildMetrics(
@@ -207,9 +206,10 @@ public class Analyzer {
return Math.round(sample.getValue());
}
- private String composeGroup(ImmutableMap<String, String> labels,
Predicate<String> filter) {
- return
labels.keySet().stream().filter(filter).sorted().map(labels::get)
- .collect(Collectors.joining("-"));
+ private DataLabel getDataLabels(ImmutableMap<String, String> labels,
Predicate<String> filter) {
+ DataLabel dataLabel = new DataLabel();
+ labels.keySet().stream().filter(filter).forEach(k -> dataLabel.put(k,
labels.get(k)));
+ return dataLabel;
}
@RequiredArgsConstructor
diff --git
a/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/AnalyzerTest.java
b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/AnalyzerTest.java
index a1868fdfc9..be3b27bdfe 100644
---
a/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/AnalyzerTest.java
+++
b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/AnalyzerTest.java
@@ -209,7 +209,7 @@ public class AnalyzerTest {
doAnswer(invocationOnMock -> {
AvgHistogramPercentileFunction actValue =
(AvgHistogramPercentileFunction) invocationOnMock.getArgument(
0, AcceptableValue.class);
- if (actValue.getSummation().hasKey("instance1:25")) {
+ if (actValue.getSummation().hasKey("{instance=instance1}:25")) {
actValues.put("instance1", actValue);
} else {
actValues.put("instance2", actValue);
@@ -233,12 +233,12 @@ public class AnalyzerTest {
});
AvgHistogramPercentileFunction instance1 = actValues.get("instance1");
AvgHistogramPercentileFunction instance2 = actValues.get("instance2");
- assertEquals(100L, instance1.getSummation().get("instance1:25"), 0.0);
- assertEquals(300L, instance1.getSummation().get("instance1:1250"),
0.0);
- assertEquals(1L, instance1.getCount().get("instance1:25"), 0.0);
- assertEquals(1L, instance1.getCount().get("instance1:1250"), 0.0);
+ assertEquals(100L,
instance1.getSummation().get("{instance=instance1}:25"), 0.0);
+ assertEquals(300L,
instance1.getSummation().get("{instance=instance1}:1250"), 0.0);
+ assertEquals(1L, instance1.getCount().get("{instance=instance1}:25"),
0.0);
+ assertEquals(1L,
instance1.getCount().get("{instance=instance1}:1250"), 0.0);
- assertEquals(122L, instance2.getSummation().get("instance2:750"), 0.0);
- assertEquals(1L, instance2.getCount().get("instance2:750"), 0.0);
+ assertEquals(122L,
instance2.getSummation().get("{instance=instance2}:750"), 0.0);
+ assertEquals(1L, instance2.getCount().get("{instance=instance2}:750"),
0.0);
}
}
diff --git
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
index 432f7b8614..ed1264912a 100644
---
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
+++
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
@@ -24,6 +24,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;
+import org.apache.skywalking.oap.server.core.analysis.metrics.DataLabel;
import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
import org.apache.skywalking.oap.server.core.query.type.Bucket;
import org.apache.skywalking.oap.server.core.query.type.HeatMap;
@@ -34,9 +35,8 @@ import
org.apache.skywalking.oap.server.core.query.type.HeatMap;
@ToString
@Getter
public class BucketedValues {
-
@Setter
- private String group;
+ private DataLabel labels = new DataLabel();
/**
* The element in the buckets represent the minimal value of this bucket,
the max is defined by the next element.
* Such as 0, 10, 50, 100 means buckets are [0, 10), [10, 50), [50, 100),
[100, infinite+).
@@ -76,7 +76,7 @@ public class BucketedValues {
if (key.equals(Bucket.INFINITE_NEGATIVE)) {
existedBuckets[i] = Long.MIN_VALUE;
} else {
- // remove the group name
+ // remove the label name
if (key.contains(":")) {
key = StringUtils.substringAfterLast(key, ":");
}
diff --git
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/PercentileFunction.java
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/PercentileFunction.java
deleted file mode 100644
index 33af4489b5..0000000000
---
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/PercentileFunction.java
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Licensed to the 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.
- * The 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 org.apache.skywalking.oap.server.core.analysis.meter.function;
-
-import org.apache.skywalking.oap.server.core.UnexpectedException;
-import org.apache.skywalking.oap.server.core.analysis.meter.Meter;
-import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
-import org.apache.skywalking.oap.server.core.analysis.metrics.DataLabel;
-import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
-import org.apache.skywalking.oap.server.core.analysis.metrics.IntList;
-import
org.apache.skywalking.oap.server.core.analysis.metrics.LabeledValueHolder;
-import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
-import
org.apache.skywalking.oap.server.core.analysis.metrics.PercentileMetrics;
-import org.apache.skywalking.oap.server.core.query.type.Bucket;
-import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
-import org.apache.skywalking.oap.server.core.storage.StorageID;
-import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
-import org.apache.skywalking.oap.server.core.storage.annotation.Column;
-import org.apache.skywalking.oap.server.core.storage.annotation.ElasticSearch;
-import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
-import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
-import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-import static
org.apache.skywalking.oap.server.core.analysis.metrics.DataLabel.PERCENTILE_LABEL_NAME;
-
-/**
- * PercentileFunction is the implementation of {@link PercentileMetrics} in
the meter system. The major difference is
- * the PercentileFunction accepts the {@link PercentileArgument} as input
rather than every single request.
- */
-@MeterFunction(functionName = "percentile")
-@Slf4j
-public abstract class PercentileFunction extends Meter implements
AcceptableValue<PercentileFunction.PercentileArgument>, LabeledValueHolder {
- public static final String DATASET = "dataset";
- public static final String RANKS = "ranks";
- public static final String VALUE = "datatable_value";
-
- @Setter
- @Getter
- @Column(name = ENTITY_ID, length = 512)
- @BanyanDB.SeriesID(index = 0)
- private String entityId;
- @Getter
- @Setter
- @Column(name = VALUE, dataType = Column.ValueDataType.LABELED_VALUE,
storageOnly = true)
- @ElasticSearch.Column(legacyName = "value")
- @BanyanDB.MeasureField
- private DataTable percentileValues = new DataTable(10);
- @Getter
- @Setter
- @Column(name = DATASET, storageOnly = true)
- @BanyanDB.MeasureField
- private DataTable dataset = new DataTable(30);
- /**
- * Rank
- */
- @Getter
- @Setter
- @Column(name = RANKS, storageOnly = true)
- @BanyanDB.MeasureField
- private IntList ranks = new IntList(10);
-
- private boolean isCalculated = false;
-
- @Override
- public void accept(final MeterEntity entity, final PercentileArgument
value) {
- if (dataset.size() > 0) {
- if (!value.getBucketedValues().isCompatible(dataset)) {
- throw new IllegalArgumentException(
- "Incompatible BucketedValues [" + value + "] for current
PercentileFunction[" + dataset + "]");
- }
- }
-
- for (final int rank : value.getRanks()) {
- if (rank <= 0) {
- throw new IllegalArgumentException("Illegal rank value " +
rank + ", must be positive");
- }
- }
-
- if (ranks.size() > 0) {
- if (ranks.size() != value.getRanks().length) {
- throw new IllegalArgumentException(
- "Incompatible ranks size = [" + value.getRanks().length +
"] for current PercentileFunction[" + ranks
- .size() + "]");
- } else {
- for (final int rank : value.getRanks()) {
- if (!ranks.include(rank)) {
- throw new IllegalArgumentException(
- "Rank " + rank + " doesn't exist in the previous
ranks " + ranks);
- }
- }
- }
- } else {
- for (final int rank : value.getRanks()) {
- ranks.add(rank);
- }
- }
-
- this.entityId = entity.id();
-
- final long[] values = value.getBucketedValues().getValues();
- for (int i = 0; i < values.length; i++) {
- final long bucket = value.getBucketedValues().getBuckets()[i];
- String bucketName = bucket == Long.MIN_VALUE ?
Bucket.INFINITE_NEGATIVE : String.valueOf(bucket);
- final long bucketValue = values[i];
- dataset.valueAccumulation(bucketName, bucketValue);
- }
-
- this.isCalculated = false;
- }
-
- @Override
- public boolean combine(final Metrics metrics) {
- PercentileFunction percentile = (PercentileFunction) metrics;
-
- if (!dataset.keysEqual(percentile.getDataset())) {
- log.warn("Incompatible input [{}}] for current
PercentileFunction[{}], entity {}",
- percentile, this, entityId
- );
- return true;
- }
- if (this.ranks.size() > 0) {
- IntList ranksOfThat = percentile.getRanks();
- if (this.ranks.size() != ranksOfThat.size()) {
- log.warn("Incompatible ranks size = [{}}] for current
PercentileFunction[{}]",
- ranksOfThat.size(), this.ranks.size()
- );
- return true;
- } else {
- if (!this.ranks.equals(ranksOfThat)) {
- log.warn("Rank {} doesn't exist in the previous ranks {}",
ranksOfThat, this.ranks);
- return true;
- }
- }
- }
-
- this.dataset.append(percentile.dataset);
-
- this.isCalculated = false;
- return true;
- }
-
- @Override
- public void calculate() {
- if (!isCalculated) {
- long total = dataset.sumOfValues();
-
- int[] roofs = new int[ranks.size()];
- for (int i = 0; i < ranks.size(); i++) {
- roofs[i] = Math.round(total * ranks.get(i) * 1.0f / 100);
- }
-
- int count = 0;
- final List<String> sortedKeys =
dataset.sortedKeys(Comparator.comparingInt(Integer::parseInt));
-
- int loopIndex = 0;
-
- for (String key : sortedKeys) {
- final Long value = dataset.get(key);
-
- count += value;
- for (int rankIdx = loopIndex; rankIdx < roofs.length;
rankIdx++) {
- int roof = roofs[rankIdx];
-
- if (count >= roof) {
- DataLabel label = new DataLabel();
- label.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
- percentileValues.put(label, Long.parseLong(key));
- loopIndex++;
- } else {
- break;
- }
- }
- }
- }
- }
-
- @Override
- public Metrics toHour() {
- PercentileFunction metrics = (PercentileFunction) createNew();
- metrics.setEntityId(getEntityId());
- metrics.setTimeBucket(toTimeBucketInHour());
- metrics.setDataset(getDataset());
- metrics.setRanks(getRanks());
- metrics.setPercentileValues(getPercentileValues());
- return metrics;
- }
-
- @Override
- public Metrics toDay() {
- PercentileFunction metrics = (PercentileFunction) createNew();
- metrics.setEntityId(getEntityId());
- metrics.setTimeBucket(toTimeBucketInDay());
- metrics.setDataset(getDataset());
- metrics.setRanks(getRanks());
- metrics.setPercentileValues(getPercentileValues());
- return metrics;
- }
-
- @Override
- public DataTable getValue() {
- return percentileValues;
- }
-
- @Override
- public int remoteHashCode() {
- return entityId.hashCode();
- }
-
- @Override
- public void deserialize(final RemoteData remoteData) {
- this.setTimeBucket(remoteData.getDataLongs(0));
-
- this.setEntityId(remoteData.getDataStrings(0));
-
- this.setDataset(new DataTable(remoteData.getDataObjectStrings(0)));
- this.setRanks(new IntList(remoteData.getDataObjectStrings(1)));
- this.setPercentileValues(new
DataTable(remoteData.getDataObjectStrings(2)));
- }
-
- @Override
- public RemoteData.Builder serialize() {
- RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
- remoteBuilder.addDataLongs(getTimeBucket());
-
- remoteBuilder.addDataStrings(entityId);
-
- remoteBuilder.addDataObjectStrings(dataset.toStorageData());
- remoteBuilder.addDataObjectStrings(ranks.toStorageData());
- remoteBuilder.addDataObjectStrings(percentileValues.toStorageData());
-
- return remoteBuilder;
- }
-
- @Override
- protected StorageID id0() {
- return new StorageID()
- .append(TIME_BUCKET, getTimeBucket())
- .append(ENTITY_ID, getEntityId());
- }
-
- @Override
- public Class<? extends StorageBuilder> builder() {
- return PercentileFunctionBuilder.class;
- }
-
- @RequiredArgsConstructor
- @Getter
- public static class PercentileArgument {
- private final BucketedValues bucketedValues;
- private final int[] ranks;
- }
-
- public static class PercentileFunctionBuilder implements
StorageBuilder<PercentileFunction> {
- @Override
- public PercentileFunction storage2Entity(final Convert2Entity
converter) {
- PercentileFunction metrics = new PercentileFunction() {
- @Override
- public AcceptableValue<PercentileArgument> createNew() {
- throw new UnexpectedException("createNew should not be
called");
- }
- };
- metrics.setDataset(new DataTable((String) converter.get(DATASET)));
- metrics.setRanks(new IntList((String) converter.get(RANKS)));
- metrics.setPercentileValues(new DataTable((String)
converter.get(VALUE)));
- metrics.setTimeBucket(((Number)
converter.get(TIME_BUCKET)).longValue());
- metrics.setEntityId((String) converter.get(ENTITY_ID));
- return metrics;
- }
-
- @Override
- public void entity2Storage(final PercentileFunction storageData, final
Convert2Storage converter) {
- converter.accept(DATASET, storageData.getDataset());
- converter.accept(RANKS, storageData.getRanks());
- converter.accept(VALUE, storageData.getPercentileValues());
- converter.accept(TIME_BUCKET, storageData.getTimeBucket());
- converter.accept(ENTITY_ID, storageData.getEntityId());
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (!(o instanceof PercentileFunction))
- return false;
- PercentileFunction function = (PercentileFunction) o;
- return Objects.equals(entityId, function.entityId) &&
- getTimeBucket() == function.getTimeBucket();
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(entityId, getTimeBucket());
- }
-}
diff --git
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunction.java
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunction.java
index 38ab223d10..570d7ab247 100644
---
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunction.java
+++
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunction.java
@@ -38,7 +38,6 @@ import
org.apache.skywalking.oap.server.core.storage.annotation.ElasticSearch;
import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
-import com.google.common.base.Strings;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import java.util.Comparator;
@@ -53,6 +52,7 @@ import static
org.apache.skywalking.oap.server.core.analysis.metrics.DataLabel.P
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.library.util.CollectionUtils;
/**
* AvgPercentile intends to calculate percentile based on the average of raw
values over the interval(minute, hour or day).
@@ -68,7 +68,6 @@ import lombok.extern.slf4j.Slf4j;
@MeterFunction(functionName = "avgHistogramPercentile")
@Slf4j
public abstract class AvgHistogramPercentileFunction extends Meter implements
AcceptableValue<PercentileArgument>, LabeledValueHolder {
- private static final String DEFAULT_GROUP = "pD";
public static final String DATASET = "dataset";
public static final String RANKS = "ranks";
public static final String VALUE = "datatable_value";
@@ -150,8 +149,8 @@ public abstract class AvgHistogramPercentileFunction
extends Meter implements Ac
this.entityId = entity.id();
String template = "%s";
- if (!Strings.isNullOrEmpty(value.getBucketedValues().getGroup())) {
- template = value.getBucketedValues().getGroup() + ":%s";
+ if (CollectionUtils.isNotEmpty(value.getBucketedValues().getLabels()))
{
+ template = value.getBucketedValues().getLabels() + ":%s";
}
final long[] values = value.getBucketedValues().getValues();
for (int i = 0; i < values.length; i++) {
@@ -207,11 +206,13 @@ public abstract class AvgHistogramPercentileFunction
extends Meter implements Ac
}
dataset.keys().stream()
.map(key -> {
+ DataLabel dataLabel = new DataLabel();
if (key.contains(":")) {
int index = key.lastIndexOf(":");
- return Tuple.of(key.substring(0, index), key);
+ dataLabel.put(key.substring(0, index));
+ return Tuple.of(dataLabel, key);
} else {
- return Tuple.of(DEFAULT_GROUP, key);
+ return Tuple.of(dataLabel, key);
}
})
.collect(groupingBy(Tuple2::_1, mapping(Tuple2::_2,
Collector.of(
@@ -228,7 +229,7 @@ public abstract class AvgHistogramPercentileFunction
extends Meter implements Ac
},
DataTable::append
))))
- .forEach((group, subDataset) -> {
+ .forEach((labels, subDataset) -> {
long total;
total = subDataset.sumOfValues();
@@ -250,13 +251,12 @@ public abstract class AvgHistogramPercentileFunction
extends Meter implements Ac
int roof = roofs[rankIdx];
if (count >= roof) {
- DataLabel label = new DataLabel();
- if (group.equals(DEFAULT_GROUP)) {
- label.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
- percentileValues.put(label,
Long.parseLong(key));
+ if (labels.isEmpty()) {
+ labels.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
+ percentileValues.put(labels,
Long.parseLong(key));
} else {
- label.put(PERCENTILE_LABEL_NAME,
String.format("%s:%s", group, ranks.get(rankIdx)));
- percentileValues.put(label,
Long.parseLong(key));
+ labels.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
+ percentileValues.put(labels,
Long.parseLong(key));
}
loopIndex++;
} else {
diff --git
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunction.java
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunction.java
index e67f317a3e..5b1af897f6 100644
---
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunction.java
+++
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunction.java
@@ -38,7 +38,6 @@ import
org.apache.skywalking.oap.server.core.storage.annotation.ElasticSearch;
import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
-import com.google.common.base.Strings;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import java.util.Comparator;
@@ -52,6 +51,7 @@ import static
org.apache.skywalking.oap.server.core.analysis.metrics.DataLabel.P
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.library.util.CollectionUtils;
/**
* SumPercentile intends to calculate percentile based on the summary of raw
values over the interval(minute, hour or day).
@@ -59,7 +59,6 @@ import lombok.extern.slf4j.Slf4j;
@MeterFunction(functionName = "sumHistogramPercentile")
@Slf4j
public abstract class SumHistogramPercentileFunction extends Meter implements
AcceptableValue<PercentileArgument>, LabeledValueHolder {
- private static final String DEFAULT_GROUP = "pD";
public static final String DATASET = "dataset";
public static final String RANKS = "ranks";
public static final String VALUE = "datatable_value";
@@ -130,8 +129,8 @@ public abstract class SumHistogramPercentileFunction
extends Meter implements Ac
this.entityId = entity.id();
String template = "%s";
- if (!Strings.isNullOrEmpty(value.getBucketedValues().getGroup())) {
- template = value.getBucketedValues().getGroup() + ":%s";
+ if (CollectionUtils.isNotEmpty(value.getBucketedValues().getLabels()))
{
+ template = value.getBucketedValues().getLabels() + ":%s";
}
final long[] values = value.getBucketedValues().getValues();
for (int i = 0; i < values.length; i++) {
@@ -174,11 +173,13 @@ public abstract class SumHistogramPercentileFunction
extends Meter implements Ac
if (!isCalculated) {
summation.keys().stream()
.map(key -> {
+ DataLabel dataLabel = new DataLabel();
if (key.contains(":")) {
int index = key.lastIndexOf(":");
- return Tuple.of(key.substring(0, index), key);
+ dataLabel.put(key.substring(0, index));
+ return Tuple.of(dataLabel, key);
} else {
- return Tuple.of(DEFAULT_GROUP, key);
+ return Tuple.of(dataLabel, key);
}
})
.collect(groupingBy(Tuple2::_1, mapping(Tuple2::_2,
Collector.of(
@@ -195,7 +196,7 @@ public abstract class SumHistogramPercentileFunction
extends Meter implements Ac
},
DataTable::append
))))
- .forEach((group, subDataset) -> {
+ .forEach((labels, subDataset) -> {
long total;
total = subDataset.sumOfValues();
@@ -217,13 +218,12 @@ public abstract class SumHistogramPercentileFunction
extends Meter implements Ac
int roof = roofs[rankIdx];
if (count >= roof) {
- DataLabel label = new DataLabel();
- if (group.equals(DEFAULT_GROUP)) {
- label.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
- percentileValues.put(label,
Long.parseLong(key));
+ if (labels.isEmpty()) {
+ labels.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
+ percentileValues.put(labels,
Long.parseLong(key));
} else {
- label.put(PERCENTILE_LABEL_NAME,
String.format("%s:%s", group, ranks.get(rankIdx)));
- percentileValues.put(label,
Long.parseLong(key));
+ labels.put(PERCENTILE_LABEL_NAME,
String.valueOf(ranks.get(rankIdx)));
+ percentileValues.put(labels,
Long.parseLong(key));
}
loopIndex++;
} else {
diff --git
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/PercentileFunctionTest.java
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/PercentileFunctionTest.java
deleted file mode 100644
index 4c9ec4951f..0000000000
---
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/PercentileFunctionTest.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Licensed to the 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.
- * The 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 org.apache.skywalking.oap.server.core.analysis.meter.function;
-
-import org.apache.skywalking.oap.server.core.analysis.Layer;
-import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
-import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
-import org.apache.skywalking.oap.server.core.analysis.metrics.IntList;
-import org.apache.skywalking.oap.server.core.config.NamingControl;
-import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
-import org.apache.skywalking.oap.server.core.storage.type.HashMapConverter;
-import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class PercentileFunctionTest {
- private static final long[] BUCKETS = new long[] {
- 0,
- 50,
- 100,
- 250
- };
-
- private static final long[] BUCKETS_2ND = new long[] {
- 0,
- 51,
- 100,
- 250
- };
-
- private static final int[] RANKS = new int[] {
- 50,
- 90
- };
-
- @BeforeAll
- public static void setup() {
- MeterEntity.setNamingControl(
- new NamingControl(512, 512, 512, new EndpointNameGrouping()));
- }
-
- @AfterAll
- public static void tearDown() {
- MeterEntity.setNamingControl(null);
- }
-
- @Test
- public void testFunction() {
- PercentileFunctionInst inst = new PercentileFunctionInst();
- inst.accept(
- MeterEntity.newService("service-test", Layer.GENERAL),
- new PercentileFunction.PercentileArgument(
- new BucketedValues(
- BUCKETS,
- new long[] {
- 10,
- 20,
- 30,
- 40
- }
- ),
- RANKS
- )
- );
-
- inst.accept(
- MeterEntity.newService("service-test", Layer.GENERAL),
- new PercentileFunction.PercentileArgument(
- new BucketedValues(
- BUCKETS,
- new long[] {
- 10,
- 20,
- 30,
- 40
- }
- ),
- RANKS
- )
- );
-
- inst.calculate();
- final DataTable values = inst.getValue();
- /**
- * Expected percentile dataset
- * <pre>
- * 0 , 20
- * 50 , 40
- * 100, 60 <- P50
- * 250, 80 <- P90
- * </pre>
- */
- Assertions.assertEquals(new DataTable("{p=50},100|{p=90},250"),
values);
- }
-
- @Test
- public void testIncompatible() {
- assertThrows(IllegalArgumentException.class, () -> {
- PercentileFunctionInst inst = new PercentileFunctionInst();
- inst.accept(
- MeterEntity.newService("service-test", Layer.GENERAL),
- new PercentileFunction.PercentileArgument(
- new BucketedValues(
- BUCKETS,
- new long[]{
- 10,
- 20,
- 30,
- 40
- }
- ),
- RANKS
- )
- );
-
- inst.accept(
- MeterEntity.newService("service-test", Layer.GENERAL),
- new PercentileFunction.PercentileArgument(
- new BucketedValues(
- BUCKETS_2ND,
- new long[]{
- 10,
- 20,
- 30,
- 40
- }
- ),
- RANKS
- )
- );
- });
- }
-
- @Test
- public void testSerialization() {
- PercentileFunctionInst inst = new PercentileFunctionInst();
- inst.accept(
- MeterEntity.newService("service-test", Layer.GENERAL),
- new PercentileFunction.PercentileArgument(
- new BucketedValues(
- BUCKETS,
- new long[] {
- 10,
- 20,
- 30,
- 40
- }
- ),
- RANKS
- )
- );
-
- PercentileFunctionInst inst2 = new PercentileFunctionInst();
- inst2.deserialize(inst.serialize().build());
-
- Assertions.assertEquals(inst, inst2);
- // HistogramFunction equal doesn't include dataset.
- Assertions.assertEquals(inst.getDataset(), inst2.getDataset());
- Assertions.assertEquals(inst.getRanks(), inst2.getRanks());
- Assertions.assertEquals(0, inst2.getPercentileValues().size());
- }
-
- @Test
- public void testBuilder() throws IllegalAccessException,
InstantiationException {
- PercentileFunctionInst inst = new PercentileFunctionInst();
- inst.accept(
- MeterEntity.newService("service-test", Layer.GENERAL),
- new PercentileFunction.PercentileArgument(
- new BucketedValues(
- BUCKETS,
- new long[] {
- 10,
- 20,
- 30,
- 40
- }
- ),
- RANKS
- )
- );
- inst.calculate();
-
- final StorageBuilder storageBuilder = inst.builder().newInstance();
-
- // Simulate the storage layer do, convert the datatable to string.
- final HashMapConverter.ToStorage hashMapConverter = new
HashMapConverter.ToStorage();
- storageBuilder.entity2Storage(inst, hashMapConverter);
- final Map<String, Object> map = hashMapConverter.obtain();
- map.put(PercentileFunction.DATASET, ((DataTable)
map.get(PercentileFunction.DATASET)).toStorageData());
- map.put(PercentileFunction.VALUE, ((DataTable)
map.get(PercentileFunction.VALUE)).toStorageData());
- map.put(PercentileFunction.RANKS, ((IntList)
map.get(PercentileFunction.RANKS)).toStorageData());
-
- final PercentileFunction inst2 = (PercentileFunction)
storageBuilder.storage2Entity(
- new HashMapConverter.ToEntity(map));
- Assertions.assertEquals(inst, inst2);
- // HistogramFunction equal doesn't include dataset.
- Assertions.assertEquals(inst.getDataset(), inst2.getDataset());
- Assertions.assertEquals(inst.getPercentileValues(),
inst2.getPercentileValues());
- Assertions.assertEquals(inst.getRanks(), inst2.getRanks());
- }
-
- private static class PercentileFunctionInst extends PercentileFunction {
- @Override
- public AcceptableValue<PercentileArgument> createNew() {
- return new PercentileFunctionInst();
- }
- }
-}
diff --git
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramFunctionTest.java
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramFunctionTest.java
index ddba0a4744..ac5bd82550 100644
---
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramFunctionTest.java
+++
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramFunctionTest.java
@@ -222,7 +222,6 @@ public class AvgHistogramFunctionTest {
10,
10
});
- bv1.setGroup("g1");
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
bv1
@@ -235,7 +234,6 @@ public class AvgHistogramFunctionTest {
3,
4
});
- bv2.setGroup("g1");
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
bv2
@@ -247,7 +245,6 @@ public class AvgHistogramFunctionTest {
6,
8
});
- bv3.setGroup("g2");
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
bv3
diff --git
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunctionTest.java
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunctionTest.java
index 9ce21be907..60a88f774a 100644
---
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunctionTest.java
+++
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunctionTest.java
@@ -198,7 +198,7 @@ public class AvgHistogramPercentileFunctionTest {
}
@Test
- public void testFunctionWhenGroupContainsColon() {
+ public void testFunctionWithLabel() {
BucketedValues valuesA = new BucketedValues(
BUCKETS,
new long[] {
@@ -208,8 +208,8 @@ public class AvgHistogramPercentileFunctionTest {
40
}
);
- valuesA.setGroup("localhost:3306/swtestA");
-
+ valuesA.getLabels().put("url", "localhost:3306/swtestA");
+ valuesA.getLabels().put("instance", "instance1");
PercentileFunctionInst inst = new PercentileFunctionInst();
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
@@ -227,8 +227,8 @@ public class AvgHistogramPercentileFunctionTest {
10
}
);
- valuesB.setGroup("localhost:3306/swtestB");
-
+ valuesB.getLabels().put("url", "localhost:3306/swtestB");
+ valuesB.getLabels().put("instance", "instance2");
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
new PercentileArgument(
@@ -250,7 +250,7 @@ public class AvgHistogramPercentileFunctionTest {
*/
assertEquals(
new DataTable(
-
"{p=localhost:3306/swtestB:50},50|{p=localhost:3306/swtestA:50},100|{p=localhost:3306/swtestB:90},100|{p=localhost:3306/swtestA:90},250"),
+
"{url=localhost:3306/swtestB,instance=instance2,p=50},50|{url=localhost:3306/swtestA,instance=instance1,p=50},100|{url=localhost:3306/swtestB,instance=instance2,p=90},100|{url=localhost:3306/swtestA,instance=instance1,p=90},250"),
values
);
}
diff --git
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunctionTest.java
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunctionTest.java
index 226e40091b..22f89ca29b 100644
---
a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunctionTest.java
+++
b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/sum/SumHistogramPercentileFunctionTest.java
@@ -105,7 +105,7 @@ public class SumHistogramPercentileFunctionTest {
* <pre>
* 0 , 20
* 50 , 40
- * 100, 50 <- P50
+ * 100, 60 <- P50
* 250, 80 <- P90
* </pre>
*/
@@ -188,7 +188,7 @@ public class SumHistogramPercentileFunctionTest {
}
@Test
- public void testFunctionWhenGroupContainsColon() {
+ public void testFunctionWithLabel() {
BucketedValues valuesA = new BucketedValues(
BUCKETS,
new long[] {
@@ -198,8 +198,8 @@ public class SumHistogramPercentileFunctionTest {
40
}
);
- valuesA.setGroup("localhost:3306/swtestA");
-
+ valuesA.getLabels().put("url", "localhost:3306/swtestA");
+ valuesA.getLabels().put("instance", "instance1");
PercentileFunctionInst inst = new PercentileFunctionInst();
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
@@ -217,8 +217,8 @@ public class SumHistogramPercentileFunctionTest {
10
}
);
- valuesB.setGroup("localhost:3306/swtestB");
-
+ valuesB.getLabels().put("url", "localhost:3306/swtestB");
+ valuesB.getLabels().put("instance", "instance2");
inst.accept(
MeterEntity.newService("service-test", Layer.GENERAL),
new PercentileArgument(
@@ -240,7 +240,7 @@ public class SumHistogramPercentileFunctionTest {
*/
assertEquals(
new DataTable(
-
"{p=localhost:3306/swtestB:50},50|{p=localhost:3306/swtestA:50},100|{p=localhost:3306/swtestB:90},100|{p=localhost:3306/swtestA:90},250"),
+
"{url=localhost:3306/swtestB,instance=instance2,p=50},50|{url=localhost:3306/swtestA,instance=instance1,p=50},100|{url=localhost:3306/swtestB,instance=instance2,p=90},100|{url=localhost:3306/swtestA,instance=instance1,p=90},250"),
values
);
}
diff --git
a/test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
b/test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
index 730fee5ef2..306128f011 100644
--- a/test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
+++ b/test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
@@ -18,8 +18,10 @@ results:
{{- contains .results }}
- metric:
labels:
+ - key: type
+ value: "apisix"
- key: p
- value: "apisix:50"
+ value: "50"
values:
{{- contains .values }}
- id: {{ notEmpty .id }}
@@ -31,8 +33,10 @@ results:
{{- end}}
- metric:
labels:
+ - key: type
+ value: "apisix"
- key: p
- value: "apisix:99"
+ value: "70"
values:
{{- contains .values }}
- id: {{ notEmpty .id }}
@@ -44,8 +48,145 @@ results:
{{- end}}
- metric:
labels:
+ - key: type
+ value: "apisix"
- key: p
- value: "request:99"
+ value: "90"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "apisix"
+ - key: p
+ value: "99"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "request"
+ - key: p
+ value: "50"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "request"
+ - key: p
+ value: "70"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "request"
+ - key: p
+ value: "90"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "request"
+ - key: p
+ value: "99"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "upstream"
+ - key: p
+ value: "50"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "upstream"
+ - key: p
+ value: "70"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "upstream"
+ - key: p
+ value: "90"
+ values:
+ {{- contains .values }}
+ - id: {{ notEmpty .id }}
+ value: {{ .value }}
+ traceid: null
+ - id: {{ notEmpty .id }}
+ value: null
+ traceid: null
+ {{- end}}
+- metric:
+ labels:
+ - key: type
+ value: "upstream"
+ - key: p
+ value: "99"
values:
{{- contains .values }}
- id: {{ notEmpty .id }}
diff --git
a/test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
b/test/e2e-v2/cases/so11y/expected/metrics-has-value-percentile.yml
similarity index 79%
copy from test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
copy to test/e2e-v2/cases/so11y/expected/metrics-has-value-percentile.yml
index 730fee5ef2..3e7fa80eb8 100644
--- a/test/e2e-v2/cases/apisix/expected/metrics-has-latency-value-label.yml
+++ b/test/e2e-v2/cases/so11y/expected/metrics-has-value-percentile.yml
@@ -19,7 +19,7 @@ results:
- metric:
labels:
- key: p
- value: "apisix:50"
+ value: "90"
values:
{{- contains .values }}
- id: {{ notEmpty .id }}
@@ -32,7 +32,7 @@ results:
- metric:
labels:
- key: p
- value: "apisix:99"
+ value: "99"
values:
{{- contains .values }}
- id: {{ notEmpty .id }}
@@ -41,19 +41,6 @@ results:
- id: {{ notEmpty .id }}
value: null
traceid: null
- {{- end}}
-- metric:
- labels:
- - key: p
- value: "request:99"
- values:
- {{- contains .values }}
- - id: {{ notEmpty .id }}
- value: {{ .value }}
- traceid: null
- - id: {{ notEmpty .id }}
- value: null
- traceid: null
- {{- end}}
-{{- end}}
+ {{- end }}
+ {{- end}}
error: null
diff --git a/test/e2e-v2/cases/so11y/so11y-cases.yaml
b/test/e2e-v2/cases/so11y/so11y-cases.yaml
index f0fda21fc3..331f0f88b2 100644
--- a/test/e2e-v2/cases/so11y/so11y-cases.yaml
+++ b/test/e2e-v2/cases/so11y/so11y-cases.yaml
@@ -44,4 +44,6 @@
expected: expected/metrics-has-value.yml
- query: swctl --display yaml
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec
--expression=meter_oap_jvm_class_total_loaded_count
--instance-name=http://localhost:1234 --service-name=oap-server
expected: expected/metrics-has-value.yml
+ - query: swctl --display yaml
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec
--expression="meter_oap_instance_trace_latency_percentile{p='90,99'}"
--instance-name=http://localhost:1234 --service-name=oap-server
+ expected: expected/metrics-has-value-percentile.yml