From 4e7d80fb88a603e076f6c53ed9817d81dc55da3d Mon Sep 17 00:00:00 2001 From: wankai123 Date: Fri, 6 Aug 2021 17:09:13 +0800 Subject: [PATCH] Support Metric level function `serviceRelation` in `MAL` and binding envoy metrics into the topology. (#7399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support Metric level function `serviceRelation` in `MAL`. * Support envoy metrics binding into the topology. * add istio args * remove istio 1.7 Co-authored-by: 吴晟 Wu Sheng Co-authored-by: Jared Tan Co-authored-by: Zhenxu --- .github/workflows/e2e.istio.yaml | 15 +- CHANGES.md | 2 + docs/en/concepts-and-designs/mal.md | 4 +- .../en/setup/envoy/metrics_service_setting.md | 23 +- .../oap/meter/analyzer/Analyzer.java | 52 ++- .../oap/meter/analyzer/dsl/DSL.java | 2 + .../EndpointEntityDescription.java | 5 - .../EntityDescription/EntityDescription.java | 6 - .../InstanceEntityDescription.java | 5 - .../ServiceEntityDescription.java | 10 - .../ServiceRelationEntityDescription.java | 43 +++ .../oap/meter/analyzer/dsl/SampleFamily.java | 108 ++++-- .../oap/meter/analyzer/dsl/ScopeTest.java | 320 +++++++++++++++--- .../envoy-svc-relation.yaml | 48 +++ .../resources/envoy-metrics-rules/envoy.yaml | 16 +- .../src/main/resources/log4j2.xml | 2 + .../resources/metadata-service-mapping.yaml | 2 +- .../ui-initialized-templates/istio-dp.yml | 34 +- .../topology-service-relation.yml | 80 +++++ .../ServiceRelationClientSideMetrics.java | 7 + .../core/analysis/meter/MeterEntity.java | 56 ++- .../server/core/analysis/meter/ScopeType.java | 3 +- .../envoy/EnvoyMetricReceiverConfig.java | 2 +- .../skywalking/e2e/metrics/MetricsQuery.java | 49 ++- .../e2e/mesh/MetricsServiceE2E.java | 75 +++- .../expected/metricsservice/topo.yml | 39 +++ 26 files changed, 840 insertions(+), 168 deletions(-) create mode 100644 oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceRelationEntityDescription.java create mode 100644 oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy-svc-relation.yaml create mode 100644 test/e2e/e2e-test/src/test/resources/expected/metricsservice/topo.yml diff --git a/.github/workflows/e2e.istio.yaml b/.github/workflows/e2e.istio.yaml index ef5c45aec..f1234148b 100644 --- a/.github/workflows/e2e.istio.yaml +++ b/.github/workflows/e2e.istio.yaml @@ -184,7 +184,7 @@ jobs: strategy: fail-fast: true matrix: - istio_version: [1.7.1, 1.8.2, 1.9.1] + istio_version: [1.8.2, 1.9.1] env: ISTIO_VERSION: ${{ matrix.istio_version }} steps: @@ -231,6 +231,14 @@ jobs: bash ${SCRIPTS_DIR}/istio.sh \ --set profile=demo \ --set meshConfig.defaultConfig.envoyMetricsService.address=skywalking-oap.istio-system:11800 \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[0]=.*membership_healthy.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[1]=.*upstream_cx_active.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[2]=.*upstream_cx_total.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[3]=.*upstream_rq_active.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[4]=.*upstream_rq_total.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[5]=.*upstream_rq_pending_active.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[6]=.*lb_healthy_panic.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[7]=.*upstream_cx_none_healthy.*' \ --set values.telemetry.v2.enabled=false # disable the metadata-exchange extension intentionally to make sure metrics service doesn't rely on it - name: Install SkyWalking @@ -262,7 +270,10 @@ jobs: - name: Deploy demo services if: env.SKIP_CI != 'true' - run: bash ${SCRIPTS_DIR}/demo.sh + run: | + bash ${SCRIPTS_DIR}/demo.sh + kubectl apply -f https://raw.githubusercontent.com/istio/istio/$ISTIO_VERSION/samples/bookinfo/networking/destination-rule-all.yaml + kubectl apply -f https://raw.githubusercontent.com/istio/istio/$ISTIO_VERSION/samples/bookinfo/networking/virtual-service-all-v1.yaml - name: Cluster Info if: ${{ failure() }} diff --git a/CHANGES.md b/CHANGES.md index 983b22fe7..abbd7afe2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,8 @@ Release Notes. 1.21. * kubernetes java client upgrade from 12.0.1 to 13.0.0 * Add `event` http receiver +* Support Metric level function `serviceRelation` in `MAL`. +* Support envoy metrics binding into the topology. #### UI diff --git a/docs/en/concepts-and-designs/mal.md b/docs/en/concepts-and-designs/mal.md index 752bd4479..a9a92a9f9 100644 --- a/docs/en/concepts-and-designs/mal.md +++ b/docs/en/concepts-and-designs/mal.md @@ -236,13 +236,15 @@ last_server_state_sync_time_in_seconds.tagEqual('production', 'catalog').downsam ## Metric level function -There are three levels in metric: service, instance and endpoint. They extract level relevant labels from metric labels, then informs the meter-system the level to which this metric belongs. +They extract level relevant labels from metric labels, then informs the meter-system the level to which this metric belongs. - `servcie([svc_label1, svc_label2...])` extracts service level labels from the array argument. - `instance([svc_label1, svc_label2...], [ins_label1, ins_label2...])` extracts service level labels from the first array argument, extracts instance level labels from the second array argument. - `endpoint([svc_label1, svc_label2...], [ep_label1, ep_label2...])` extracts service level labels from the first array argument, extracts endpoint level labels from the second array argument. + - `serviceRelation(DetectPoint, [source_svc_label1...], [dest_svc_label1...])` DetectPoint including `DetectPoint.CLIENT` and `DetectPoint.SERVER`, + extracts `sourceService` labels from the first array argument, extracts `destService` labels from the second array argument. ## More Examples diff --git a/docs/en/setup/envoy/metrics_service_setting.md b/docs/en/setup/envoy/metrics_service_setting.md index a1ca6fd37..6574ba8b2 100644 --- a/docs/en/setup/envoy/metrics_service_setting.md +++ b/docs/en/setup/envoy/metrics_service_setting.md @@ -72,7 +72,7 @@ Under this circumstance, emitting the metrics to SyWalking is as simple as addin ```shell istioctl install -y \ - --set profile=demo `# replace the profile as per your need` \ + --set profile=demo # replace the profile as per your need \ --set meshConfig.defaultConfig.envoyMetricsService.address= \ # replace with your actual SkyWalking OAP address --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[0]=.*' ``` @@ -81,11 +81,30 @@ If you already have Istio installed, you can use the following command to apply ```shell istioctl manifest install -y \ - --set profile=demo `# replace the profile as per your need` \ + --set profile=demo # replace the profile as per your need \ --set meshConfig.defaultConfig.envoyMetricsService.address= \ # replace with your actual SkyWalking OAP address --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[0]=.*' ``` +Note: +`proxyStatsMatcher` only supported by `Istio 1.8+`. +We recommend using `inclusionRegexps` to reserve the specific metrics which need to analyze that can reduce Memory and CPU overhead. +For example, OAP used these metrics: + +```shell +istioctl manifest install -y \ + --set profile=demo # replace the profile as per your need \ + --set meshConfig.defaultConfig.envoyMetricsService.address= \ # replace with your actual SkyWalking OAP address + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[0]=.*membership_healthy.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[1]=.*upstream_cx_active.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[2]=.*upstream_cx_total.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[3]=.*upstream_rq_active.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[4]=.*upstream_rq_total.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[5]=.*upstream_rq_pending_active.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[6]=.*lb_healthy_panic.*' \ + --set 'meshConfig.defaultConfig.proxyStatsMatcher.inclusionRegexps[7]=.*upstream_cx_none_healthy.*' +``` + # Metrics data Some Envoy statistics are listed in this [list](https://www.envoyproxy.io/docs/envoy/v1.17.0/configuration/upstream/cluster_manager/cluster_stats#config-cluster-manager-cluster-stats). A sample data that contains identifier can be found [here](identify.json), while the metrics only can be observed [here](metrics.json). 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 4b9975e78..5364b0cbb 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 @@ -39,6 +39,8 @@ import org.apache.skywalking.oap.server.core.analysis.NodeType; import org.apache.skywalking.oap.server.core.analysis.TimeBucket; import org.apache.skywalking.oap.server.core.analysis.manual.endpoint.EndpointTraffic; import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.service.ServiceRelationClientSideMetrics; +import org.apache.skywalking.oap.server.core.analysis.manual.relation.service.ServiceRelationServerSideMetrics; import org.apache.skywalking.oap.server.core.analysis.manual.service.ServiceTraffic; import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity; import org.apache.skywalking.oap.server.core.analysis.meter.MeterSystem; @@ -240,11 +242,23 @@ public class Analyzer { } private void generateTraffic(MeterEntity entity) { - ServiceTraffic s = new ServiceTraffic(); - s.setName(requireNonNull(entity.getServiceName())); - s.setNodeType(NodeType.Normal); - s.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); - MetricsStreamProcessor.getInstance().in(s); + if (entity.getDetectPoint() != null) { + switch (entity.getDetectPoint()) { + case SERVER: + entity.setServiceName(entity.getDestServiceName()); + toService(requireNonNull(entity.getDestServiceName())); + serverSide(entity); + break; + case CLIENT: + entity.setServiceName(entity.getSourceServiceName()); + toService(requireNonNull(entity.getSourceServiceName())); + clientSide(entity); + break; + } + } else { + toService(requireNonNull(entity.getServiceName())); + } + if (!com.google.common.base.Strings.isNullOrEmpty(entity.getInstanceName())) { InstanceTraffic instanceTraffic = new InstanceTraffic(); instanceTraffic.setName(entity.getInstanceName()); @@ -261,4 +275,32 @@ public class Analyzer { MetricsStreamProcessor.getInstance().in(endpointTraffic); } } + + private void toService(String serviceName) { + ServiceTraffic s = new ServiceTraffic(); + s.setName(requireNonNull(serviceName)); + s.setNodeType(NodeType.Normal); + s.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + MetricsStreamProcessor.getInstance().in(s); + } + + private void serverSide(MeterEntity entity) { + ServiceRelationServerSideMetrics metrics = new ServiceRelationServerSideMetrics(); + metrics.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + metrics.setSourceServiceId(entity.sourceServiceId()); + metrics.setDestServiceId(entity.destServiceId()); + metrics.setComponentId(0); + metrics.setEntityId(entity.id()); + MetricsStreamProcessor.getInstance().in(metrics); + } + + private void clientSide(MeterEntity entity) { + ServiceRelationClientSideMetrics metrics = new ServiceRelationClientSideMetrics(); + metrics.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis())); + metrics.setSourceServiceId(entity.sourceServiceId()); + metrics.setDestServiceId(entity.destServiceId()); + metrics.setComponentId(0); + metrics.setEntityId(entity.id()); + MetricsStreamProcessor.getInstance().in(metrics); + } } diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java index 65a93aeb7..bb8c73755 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java @@ -22,6 +22,7 @@ import groovy.lang.Binding; import groovy.lang.GroovyShell; import groovy.util.DelegatingScript; import org.apache.skywalking.oap.meter.analyzer.dsl.tagOpt.K8sRetagType; +import org.apache.skywalking.oap.server.core.source.DetectPoint; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; @@ -41,6 +42,7 @@ public final class DSL { cc.setScriptBaseClass(DelegatingScript.class.getName()); ImportCustomizer icz = new ImportCustomizer(); icz.addImport("K8sRetagType", K8sRetagType.class.getName()); + icz.addImport("DetectPoint", DetectPoint.class.getName()); cc.addCompilationCustomizers(icz); GroovyShell sh = new GroovyShell(new Binding(), cc); DelegatingScript script = (DelegatingScript) sh.parse(expression); diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EndpointEntityDescription.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EndpointEntityDescription.java index 15088cd1a..a7aa25d0b 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EndpointEntityDescription.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EndpointEntityDescription.java @@ -38,9 +38,4 @@ public class EndpointEntityDescription implements EntityDescription { public List getLabelKeys() { return Stream.concat(this.serviceKeys.stream(), this.endpointKeys.stream()).collect(Collectors.toList()); } - - @Override - public List getInstanceKeys() { - throw new UnsupportedOperationException("Unsupported Operation of getInstanceKeys() " + this.toString()); - } } diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EntityDescription.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EntityDescription.java index 036d71fca..24e09090f 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EntityDescription.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/EntityDescription.java @@ -25,10 +25,4 @@ public interface EntityDescription { ScopeType getScopeType(); List getLabelKeys(); - - List getServiceKeys(); - - List getInstanceKeys(); - - List getEndpointKeys(); } diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java index c80156959..1e6013ba3 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java @@ -38,9 +38,4 @@ public class InstanceEntityDescription implements EntityDescription { public List getLabelKeys() { return Stream.concat(this.serviceKeys.stream(), this.instanceKeys.stream()).collect(Collectors.toList()); } - - @Override - public List getEndpointKeys() { - throw new UnsupportedOperationException("Unsupported Operation of getEndpointKeys() " + this.toString()); - } } diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceEntityDescription.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceEntityDescription.java index edbc6e330..c96c2b842 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceEntityDescription.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceEntityDescription.java @@ -35,14 +35,4 @@ public class ServiceEntityDescription implements EntityDescription { public List getLabelKeys() { return serviceKeys; } - - @Override - public List getInstanceKeys() { - throw new UnsupportedOperationException("Unsupported Operation of getInstanceKeys() " + this.toString()); - } - - @Override - public List getEndpointKeys() { - throw new UnsupportedOperationException("Unsupported Operation of getEndpointKeys() " + this.toString()); - } } diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceRelationEntityDescription.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceRelationEntityDescription.java new file mode 100644 index 000000000..a00f9eb84 --- /dev/null +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/ServiceRelationEntityDescription.java @@ -0,0 +1,43 @@ +/* + * 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.meter.analyzer.dsl.EntityDescription; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.apache.skywalking.oap.server.core.analysis.meter.ScopeType; +import org.apache.skywalking.oap.server.core.source.DetectPoint; + +@Getter +@RequiredArgsConstructor +@ToString +public class ServiceRelationEntityDescription implements EntityDescription { + private final ScopeType scopeType = ScopeType.SERVICE_RELATION; + private final List sourceServiceKeys; + private final List destServiceKeys; + private final DetectPoint detectPoint; + + @Override + public List getLabelKeys() { + return Stream.concat(this.sourceServiceKeys.stream(), this.destServiceKeys.stream()).collect(Collectors.toList()); + } +} diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java index bfacea461..c15303308 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java @@ -38,6 +38,7 @@ import org.apache.skywalking.oap.meter.analyzer.dsl.EntityDescription.EndpointEn import org.apache.skywalking.oap.meter.analyzer.dsl.EntityDescription.EntityDescription; import org.apache.skywalking.oap.meter.analyzer.dsl.EntityDescription.InstanceEntityDescription; import org.apache.skywalking.oap.meter.analyzer.dsl.EntityDescription.ServiceEntityDescription; +import org.apache.skywalking.oap.meter.analyzer.dsl.EntityDescription.ServiceRelationEntityDescription; import org.apache.skywalking.oap.meter.analyzer.dsl.tagOpt.K8sRetagType; import org.apache.skywalking.oap.server.core.UnexpectedException; import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity; @@ -56,6 +57,7 @@ import java.util.function.DoubleBinaryOperator; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.skywalking.oap.server.core.source.DetectPoint; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.function.UnaryOperator.identity; @@ -213,7 +215,8 @@ public class SampleFamily { } if (by == null) { double result = Arrays.stream(samples).mapToDouble(Sample::getValue).average().orElse(0.0D); - return SampleFamily.build(this.context, InternalOps.newSample(samples[0].name, ImmutableMap.of(), samples[0].timestamp, result)); + return SampleFamily.build( + this.context, InternalOps.newSample(samples[0].name, ImmutableMap.of(), samples[0].timestamp, result)); } return SampleFamily.build( @@ -238,7 +241,8 @@ public class SampleFamily { } if (by == null) { double result = Arrays.stream(samples).mapToDouble(s -> s.value).reduce(aggregator).orElse(0.0D); - return SampleFamily.build(this.context, InternalOps.newSample(samples[0].name, ImmutableMap.of(), samples[0].timestamp, result)); + return SampleFamily.build( + this.context, InternalOps.newSample(samples[0].name, ImmutableMap.of(), samples[0].timestamp, result)); } return SampleFamily.build( this.context, @@ -320,15 +324,19 @@ public class SampleFamily { Object r = c.call(arg); return sample.toBuilder() .labels( - ImmutableMap.copyOf(Optional.ofNullable((r instanceof Map) ? (Map) r : null) - .orElse(arg))) + ImmutableMap.copyOf( + Optional.ofNullable((r instanceof Map) ? (Map) r : null) + .orElse(arg))) .build(); }).toArray(Sample[]::new) ); } /* k8s retags*/ - public SampleFamily retagByK8sMeta(String newLabelName, K8sRetagType type, String existingLabelName, String namespaceLabelName) { + public SampleFamily retagByK8sMeta(String newLabelName, + K8sRetagType type, + String existingLabelName, + String namespaceLabelName) { Preconditions.checkArgument(!Strings.isNullOrEmpty(newLabelName)); Preconditions.checkArgument(!Strings.isNullOrEmpty(existingLabelName)); Preconditions.checkArgument(!Strings.isNullOrEmpty(namespaceLabelName)); @@ -337,7 +345,8 @@ public class SampleFamily { return EMPTY; } - return SampleFamily.build(this.context, type.execute(samples, newLabelName, existingLabelName, namespaceLabelName)); + return SampleFamily.build( + this.context, type.execute(samples, newLabelName, existingLabelName, namespaceLabelName)); } public SampleFamily histogram() { @@ -368,8 +377,17 @@ public class SampleFamily { double r = this.context.histogramType == HistogramType.ORDINARY ? s.value : s.value - pre.get(); pre.set(s.value); ImmutableMap ll = ImmutableMap.builder() - .putAll(Maps.filterKeys(s.labels, key -> !Objects.equals(key, le))) - .put("le", String.valueOf((long) ((Double.parseDouble(this.context.histogramType == HistogramType.ORDINARY ? s.labels.get(le) : preLe.get())) * scale))).build(); + .putAll(Maps.filterKeys(s.labels, + key -> !Objects.equals( + key, le) + )) + .put( + "le", String.valueOf( + (long) ((Double.parseDouble( + this.context.histogramType == HistogramType.ORDINARY ? s.labels + .get( + le) : preLe.get())) * scale))) + .build(); preLe.set(s.labels.get(le)); return InternalOps.newSample(s.name, ll, s.timestamp, r); }) @@ -381,7 +399,8 @@ public class SampleFamily { Preconditions.checkArgument(percentiles.size() > 0); int[] p = percentiles.stream().mapToInt(i -> i).toArray(); ExpressionParsingContext.get().ifPresent(ctx -> { - Preconditions.checkState(ctx.isHistogram, "histogram() should be invoked before invoking histogram_percentile()"); + Preconditions.checkState( + ctx.isHistogram, "histogram() should be invoked before invoking histogram_percentile()"); ctx.percentiles = p; }); return this; @@ -402,7 +421,6 @@ public class SampleFamily { public SampleFamily instance(List serviceKeys, List instanceKeys) { Preconditions.checkArgument(serviceKeys.size() > 0); Preconditions.checkArgument(instanceKeys.size() > 0); - ExpressionParsingContext.get().ifPresent(ctx -> ctx.scopeType = ScopeType.SERVICE_INSTANCE); ExpressionParsingContext.get().ifPresent(ctx -> { ctx.scopeType = ScopeType.SERVICE_INSTANCE; ctx.scopeLabels.addAll(serviceKeys); @@ -417,7 +435,6 @@ public class SampleFamily { public SampleFamily endpoint(List serviceKeys, List endpointKeys) { Preconditions.checkArgument(serviceKeys.size() > 0); Preconditions.checkArgument(endpointKeys.size() > 0); - ExpressionParsingContext.get().ifPresent(ctx -> ctx.scopeType = ScopeType.ENDPOINT); ExpressionParsingContext.get().ifPresent(ctx -> { ctx.scopeType = ScopeType.ENDPOINT; ctx.scopeLabels.addAll(serviceKeys); @@ -429,10 +446,26 @@ public class SampleFamily { return createMeterSamples(new EndpointEntityDescription(serviceKeys, endpointKeys)); } + public SampleFamily serviceRelation(DetectPoint detectPoint, List sourceServiceKeys, List destServiceKeys) { + Preconditions.checkArgument(sourceServiceKeys.size() > 0); + Preconditions.checkArgument(destServiceKeys.size() > 0); + ExpressionParsingContext.get().ifPresent(ctx -> { + ctx.scopeType = ScopeType.SERVICE_RELATION; + ctx.scopeLabels.addAll(sourceServiceKeys); + ctx.scopeLabels.addAll(destServiceKeys); + }); + if (this == EMPTY) { + return EMPTY; + } + return createMeterSamples(new ServiceRelationEntityDescription(sourceServiceKeys, destServiceKeys, detectPoint)); + } + private SampleFamily createMeterSamples(EntityDescription entityDescription) { Map meterSamples = new HashMap<>(); Arrays.stream(samples) - .collect(groupingBy(it -> InternalOps.getLabels(entityDescription.getLabelKeys(), it), mapping(identity(), toList()))) + .collect(groupingBy(it -> InternalOps.getLabels(entityDescription.getLabelKeys(), it), + mapping(identity(), toList()) + )) .forEach((labels, samples) -> { MeterEntity meterEntity = InternalOps.buildMeterEntity(samples, entityDescription); meterSamples.put(meterEntity, InternalOps.left(samples, entityDescription.getLabelKeys())); @@ -452,7 +485,10 @@ public class SampleFamily { Sample[] ss = Arrays.stream(samples) .filter(sample -> ll.entrySet() .stream() - .allMatch(entry -> op.apply(sample.labels.getOrDefault(entry.getKey(), ""), entry.getValue()))) + .allMatch( + entry -> op.apply(sample.labels.getOrDefault(entry.getKey(), ""), + entry.getValue() + ))) .toArray(Sample[]::new); return ss.length > 0 ? SampleFamily.build(this.context, ss) : EMPTY; } @@ -480,7 +516,10 @@ public class SampleFamily { Sample[] ss = Arrays.stream(samples) .flatMap(cs -> io.vavr.collection.Stream.of(another.samples) .find(as -> cs.labels.equals(as.labels)) - .map(as -> cs.toBuilder().value(transform.apply(cs.value, as.value))) + .map(as -> cs.toBuilder() + .value(transform.apply(cs.value, + as.value + ))) .map(Sample.SampleBuilder::build) .toJavaStream()) .toArray(Sample[]::new); @@ -506,9 +545,9 @@ public class SampleFamily { static RunningContext instance() { return RunningContext.builder() - .histogramType(HistogramType.CUMULATIVE) - .defaultHistogramBucketUnit(TimeUnit.SECONDS) - .build(); + .histogramType(HistogramType.CUMULATIVE) + .defaultHistogramBucketUnit(TimeUnit.SECONDS) + .build(); } private Map meterSamples = new HashMap<>(); @@ -523,8 +562,10 @@ public class SampleFamily { private static Sample[] left(List samples, List labelKeys) { return samples.stream().map(s -> { ImmutableMap ll = ImmutableMap.builder() - .putAll(Maps.filterKeys(s.labels, key -> !labelKeys.contains(key))) - .build(); + .putAll(Maps.filterKeys(s.labels, + key -> !labelKeys.contains(key) + )) + .build(); return s.toBuilder().labels(ll).build(); }).toArray(Sample[]::new); } @@ -540,22 +581,37 @@ public class SampleFamily { EntityDescription entityDescription) { switch (entityDescription.getScopeType()) { case SERVICE: - return MeterEntity.newService(InternalOps.dim(samples, entityDescription.getServiceKeys())); + ServiceEntityDescription serviceEntityDescription = (ServiceEntityDescription) entityDescription; + return MeterEntity.newService(InternalOps.dim(samples, serviceEntityDescription.getServiceKeys())); case SERVICE_INSTANCE: + InstanceEntityDescription instanceEntityDescription = (InstanceEntityDescription) entityDescription; return MeterEntity.newServiceInstance( - InternalOps.dim(samples, entityDescription.getServiceKeys()), - InternalOps.dim(samples, entityDescription.getInstanceKeys()) + InternalOps.dim(samples, instanceEntityDescription.getServiceKeys()), + InternalOps.dim(samples, instanceEntityDescription.getInstanceKeys()) ); case ENDPOINT: + EndpointEntityDescription endpointEntityDescription = (EndpointEntityDescription) entityDescription; return MeterEntity.newEndpoint( - InternalOps.dim(samples, entityDescription.getServiceKeys()), - InternalOps.dim(samples, entityDescription.getEndpointKeys()) + InternalOps.dim(samples, endpointEntityDescription.getServiceKeys()), + InternalOps.dim(samples, endpointEntityDescription.getEndpointKeys()) ); - default: throw new UnexpectedException("Unexpected scope type of entityDescription " + entityDescription.toString()); + case SERVICE_RELATION: + ServiceRelationEntityDescription serviceRelationEntityDescription = (ServiceRelationEntityDescription) entityDescription; + return MeterEntity.newServiceRelation( + InternalOps.dim(samples, serviceRelationEntityDescription.getSourceServiceKeys()), + InternalOps.dim(samples, serviceRelationEntityDescription.getDestServiceKeys()), + serviceRelationEntityDescription.getDetectPoint() + ); + default: + throw new UnexpectedException( + "Unexpected scope type of entityDescription " + entityDescription.toString()); } } - private static Sample newSample(String name, ImmutableMap labels, long timestamp, double newValue) { + private static Sample newSample(String name, + ImmutableMap labels, + long timestamp, + double newValue) { return Sample.builder() .value(newValue) .labels(labels) diff --git a/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ScopeTest.java b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ScopeTest.java index fcafba5ab..6fb6c070d 100644 --- a/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ScopeTest.java +++ b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ScopeTest.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity; +import org.apache.skywalking.oap.server.core.source.DetectPoint; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -60,10 +61,26 @@ public class ScopeTest { "sum_service", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['idc']).service(['idc'])", false, @@ -84,10 +101,26 @@ public class ScopeTest { "sum_service_labels", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['region', 'idc']).service(['idc'])", false, @@ -96,13 +129,24 @@ public class ScopeTest { put( MeterEntity.newService("t1"), new Sample[] { - Sample.builder().labels(of("region", "")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("region", "us")).value(150).name("http_success_request").build() + Sample.builder() + .labels(of("region", "")) + .value(50) + .name("http_success_request").build(), + Sample.builder() + .labels(of("region", "us")) + .value(150) + .name("http_success_request").build() } ); put( MeterEntity.newService("t3"), - new Sample[] {Sample.builder().labels(of("region", "cn")).value(54).name("http_success_request").build()} + new Sample[] { + Sample.builder() + .labels(of("region", "cn")) + .value(54) + .name("http_success_request").build() + } ); } } @@ -111,10 +155,26 @@ public class ScopeTest { "sum_service_m", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['idc', 'region']).service(['idc' , 'region'])", false, @@ -139,10 +199,26 @@ public class ScopeTest { "sum_service_endpiont", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['region', 'idc']).endpoint(['idc'] , ['region'])", false, @@ -168,10 +244,26 @@ public class ScopeTest { "sum_service_endpiont_labels", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['region', 'idc' , 'instance']).endpoint(['idc'] , ['region'])", false, @@ -180,20 +272,37 @@ public class ScopeTest { put( MeterEntity.newEndpoint("t1", "us"), new Sample[] { - Sample.builder().labels(of("instance", "")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("instance", "10.0.0.1")).value(100).name("http_success_request").build() + Sample.builder() + .labels(of("instance", "")) + .value(50) + .name("http_success_request").build(), + Sample.builder() + .labels(of("instance", "10.0.0.1")) + .value(100) + .name("http_success_request").build() } ); put( MeterEntity.newEndpoint("t3", "cn"), new Sample[] { - Sample.builder().labels(of("instance", "")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("instance", "")) + .value(51) + .name("http_success_request").build(), + Sample.builder() + .labels(of("instance", "10.0.0.1")) + .value(3) + .name("http_success_request").build() } ); put( MeterEntity.newEndpoint("t1", ""), - new Sample[] {Sample.builder().labels(of("instance", "")).value(50).name("http_success_request").build()} + new Sample[] { + Sample.builder() + .labels(of("instance", "")) + .value(50) + .name("http_success_request").build() + } ); } } @@ -202,8 +311,16 @@ public class ScopeTest { "sum_service_endpiont_labels_m", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "product")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "catalog")).value(50).name("http_success_request").build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "product")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "catalog")) + .value(50) + .name("http_success_request") + .build(), Sample.builder() .labels(of("idc", "t1", "region", "us", "svc", "catalog", "instance", "10.0.0.1")) .value(100) @@ -222,20 +339,37 @@ public class ScopeTest { put( MeterEntity.newEndpoint("t1", "us.catalog"), new Sample[] { - Sample.builder().labels(of("instance", "")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("instance", "10.0.0.1")).value(100).name("http_success_request").build() + Sample.builder() + .labels(of("instance", "")) + .value(50) + .name("http_success_request").build(), + Sample.builder() + .labels(of("instance", "10.0.0.1")) + .value(100) + .name("http_success_request").build() } ); put( MeterEntity.newEndpoint("t3", "cn.product"), new Sample[] { - Sample.builder().labels(of("instance", "")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("instance", "")) + .value(51) + .name("http_success_request").build(), + Sample.builder() + .labels(of("instance", "10.0.0.1")) + .value(3) + .name("http_success_request").build() } ); put( MeterEntity.newEndpoint("t1", ""), - new Sample[] {Sample.builder().labels(of("instance", "")).value(50).name("http_success_request").build()} + new Sample[] { + Sample.builder() + .labels(of("instance", "")) + .value(50) + .name("http_success_request").build() + } ); } } @@ -244,10 +378,26 @@ public class ScopeTest { "sum_service_instance", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['region', 'idc']).instance(['idc'] , ['region'])", false, @@ -272,10 +422,26 @@ public class ScopeTest { "sum_service_instance_labels", of("http_success_request", SampleFamilyBuilder.newBuilder( Sample.builder().labels(of("idc", "t1")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "svc", "catalog")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "svc", "product")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")).value(100).name("http_success_request").build(), - Sample.builder().labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "svc", "catalog")) + .value(51) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "svc", "product")) + .value(50) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t1", "region", "us", "instance", "10.0.0.1")) + .value(100) + .name("http_success_request") + .build(), + Sample.builder() + .labels(of("idc", "t3", "region", "cn", "instance", "10.0.0.1")) + .value(3) + .name("http_success_request") + .build() ).build()), "http_success_request.sum(['region', 'idc' , 'instance']).instance(['idc'] , ['region'])", false, @@ -284,25 +450,81 @@ public class ScopeTest { put( MeterEntity.newServiceInstance("t1", "us"), new Sample[] { - Sample.builder().labels(of("instance", "")).value(50).name("http_success_request").build(), - Sample.builder().labels(of("instance", "10.0.0.1")).value(100).name("http_success_request").build() + Sample.builder() + .labels(of("instance", "")) + .value(50) + .name("http_success_request").build(), + Sample.builder() + .labels(of("instance", "10.0.0.1")) + .value(100) + .name("http_success_request").build() } ); put( MeterEntity.newServiceInstance("t3", "cn"), new Sample[] { - Sample.builder().labels(of("instance", "")).value(51).name("http_success_request").build(), - Sample.builder().labels(of("instance", "10.0.0.1")).value(3).name("http_success_request").build() + Sample.builder() + .labels(of("instance", "")) + .value(51) + .name("http_success_request").build(), + Sample.builder() + .labels(of("instance", "10.0.0.1")) + .value(3) + .name("http_success_request").build() } ); put( MeterEntity.newServiceInstance("t1", ""), - new Sample[] {Sample.builder().labels(of("instance", "")).value(50).name("http_success_request").build()} + new Sample[] { + Sample.builder() + .labels(of("instance", "")) + .value(50) + .name("http_success_request").build() + } ); } } }, - }); + { + "sum_service_relation", + of("envoy_cluster_metrics_up_cx_active", SampleFamilyBuilder.newBuilder( + Sample.builder() + .labels(of("app", "productpage", "cluster_name", "details")) + .value(11) + .name("envoy_cluster_metrics_up_cx_active") + .build(), + Sample.builder() + .labels(of("app", "productpage", "cluster_name", "reviews")) + .value(16) + .name("envoy_cluster_metrics_up_cx_active") + .build() + ).build()), + "envoy_cluster_metrics_up_cx_active.sum(['app' ,'cluster_name']).serviceRelation(DetectPoint.CLIENT, ['app'], ['cluster_name'])", + false, + new HashMap() { + { + put( + MeterEntity.newServiceRelation("productpage", "details", DetectPoint.CLIENT), + new Sample[] { + Sample.builder() + .labels(of()) + .value(11) + .name("envoy_cluster_metrics_up_cx_active").build() + } + ); + put( + MeterEntity.newServiceRelation("productpage", "reviews", DetectPoint.CLIENT), + new Sample[] { + Sample.builder() + .labels(of()) + .value(16) + .name("envoy_cluster_metrics_up_cx_active").build() + } + ); + } + } + } + }); } @Test diff --git a/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy-svc-relation.yaml b/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy-svc-relation.yaml new file mode 100644 index 000000000..a682edc75 --- /dev/null +++ b/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy-svc-relation.yaml @@ -0,0 +1,48 @@ +# 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. + +# This will parse a textual representation of a duration. The formats +# accepted are based on the ISO-8601 duration format {@code PnDTnHnMn.nS} +# with days considered to be exactly 24 hours. +#

+# Examples: +#

+#    "PT20.345S" -- parses as "20.345 seconds"
+#    "PT15M"     -- parses as "15 minutes" (where a minute is 60 seconds)
+#    "PT10H"     -- parses as "10 hours" (where an hour is 3600 seconds)
+#    "P2D"       -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
+#    "P2DT3H4M"  -- parses as "2 days, 3 hours and 4 minutes"
+#    "P-6H3M"    -- parses as "-6 hours and +3 minutes"
+#    "-P6H3M"    -- parses as "-6 hours and -3 minutes"
+#    "-P-6H+3M"  -- parses as "+6 hours and -3 minutes"
+# 
+ +expSuffix: serviceRelation(DetectPoint.CLIENT, ['app'], ['cluster_name']) +metricPrefix: envoy_sr +metricsRules: + - name: cluster_up_cx_active + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_cx_active').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app' ,'cluster_name']) + - name: cluster_up_cx_incr + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_cx_total').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app' , 'cluster_name']).increase('PT1M') + - name: cluster_up_rq_active + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_rq_active').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app', 'cluster_name']) + - name: cluster_up_rq_incr + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_rq_total').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app' , 'cluster_name']).increase('PT1M') + - name: cluster_up_rq_pending_active + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_rq_pending_active').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app' , 'cluster_name']) + - name: cluster_lb_healthy_panic_incr + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+lb_healthy_panic').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app', 'cluster_name']).increase('PT1M') + - name: cluster_up_cx_none_healthy_incr + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_cx_none_healthy').tagMatch('metrics_name' , 'cluster.outbound.+').sum(['app', 'cluster_name']).increase('PT1M') diff --git a/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy.yaml b/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy.yaml index 787025a03..db3e5e94c 100644 --- a/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy.yaml +++ b/oap-server/server-bootstrap/src/main/resources/envoy-metrics-rules/envoy.yaml @@ -61,17 +61,17 @@ metricsRules: # envoy_cluster_metrics - name: cluster_membership_healthy exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+membership_healthy').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').tagNotMatch('cluster_name' , '.+kube-system').sum(['app', 'instance' , 'cluster_name']) - - name: cluster_upstream_cx_active + - name: cluster_up_cx_active exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_cx_active').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']) - - name: cluster_upstream_cx_increase + - name: cluster_up_cx_incr exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_cx_total').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']).increase('PT1M') - - name: cluster_upstream_rq_active + - name: cluster_up_rq_active exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_rq_active').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']) - - name: cluster_upstream_rq_increase + - name: cluster_up_rq_incr exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_rq_total').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']).increase('PT1M') - - name: cluster_upstream_rq_pending_active + - name: cluster_up_rq_pending_active exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_rq_pending_active').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']) - - name: cluster_lb_healthy_panic_increase - exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+lb_healthy_panic').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']) - - name: cluster_upstream_cx_none_healthy_increase + - name: cluster_lb_healthy_panic_incr + exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+lb_healthy_panic').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']).increase('PT1M') + - name: cluster_up_cx_none_healthy_incr exp: envoy_cluster_metrics.tagMatch('metrics_name' , '.+upstream_cx_none_healthy').tagMatch('metrics_name' , 'cluster.outbound.+|cluster.inbound.+').sum(['app', 'instance' , 'cluster_name']).increase('PT1M') diff --git a/oap-server/server-bootstrap/src/main/resources/log4j2.xml b/oap-server/server-bootstrap/src/main/resources/log4j2.xml index a14272fd3..92c888409 100644 --- a/oap-server/server-bootstrap/src/main/resources/log4j2.xml +++ b/oap-server/server-bootstrap/src/main/resources/log4j2.xml @@ -37,6 +37,8 @@ + + diff --git a/oap-server/server-bootstrap/src/main/resources/metadata-service-mapping.yaml b/oap-server/server-bootstrap/src/main/resources/metadata-service-mapping.yaml index acb4d430a..7737432db 100644 --- a/oap-server/server-bootstrap/src/main/resources/metadata-service-mapping.yaml +++ b/oap-server/server-bootstrap/src/main/resources/metadata-service-mapping.yaml @@ -13,5 +13,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -serviceName: ${LABELS."service.istio.io/canonical-revision"}.${LABELS."service.istio.io/canonical-name",LABELS."app.kubernetes.io/name",LABELS.app}.${NAMESPACE} +serviceName: ${LABELS."service.istio.io/canonical-name",LABELS."app.kubernetes.io/name",LABELS.app} serviceInstanceName: ${NAME} diff --git a/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/istio-dp.yml b/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/istio-dp.yml index 2c674caa3..b70646045 100644 --- a/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/istio-dp.yml +++ b/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/istio-dp.yml @@ -86,58 +86,58 @@ templates: }, { "width": "3", - "title": "Upstream CX Active", + "title": "Upstream Connection Active", "height": 350, "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_upstream_cx_active", + "metricName": "envoy_cluster_up_cx_active", "queryMetricType": "readLabeledMetricsValues", "chartType": "ChartLine" }, { "width": "3", - "title": "Upstream CX Increase", + "title": "Upstream Connection Increase", "height": 350, "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_upstream_cx_increase", + "metricName": "envoy_cluster_up_cx_incr", "queryMetricType": "readLabeledMetricsValues", - "unit": "pm", + "unit": "Per Minute", "chartType": "ChartLine" }, { "width": "3", - "title": "Upstream RQ Active", + "title": "Upstream Request Active", "height": 350, "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_upstream_rq_active", + "metricName": "envoy_cluster_up_rq_active", "queryMetricType": "readLabeledMetricsValues", "chartType": "ChartLine" }, { "width": "3", - "title": "Upstream RQ Increase", + "title": "Upstream Request Increase", "height": 350, "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_upstream_rq_increase", + "metricName": "envoy_cluster_up_rq_incr", "queryMetricType": "readLabeledMetricsValues", - "unit": "pm", + "unit": "Per Minute", "chartType": "ChartLine" }, { "width": "3", - "title": "Upstream RQ Pending Active", + "title": "Upstream Request Pending Active", "height": 350, "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_upstream_rq_pending_active", + "metricName": "envoy_cluster_up_rq_pending_active", "queryMetricType": "readLabeledMetricsValues", "chartType": "ChartLine" }, @@ -148,21 +148,21 @@ templates: "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_lb_healthy_panic_increase", + "metricName": "envoy_cluster_lb_healthy_panic_incr", "queryMetricType": "readLabeledMetricsValues", - "unit": "pm", + "unit": "Per Minute", "chartType": "ChartLine" }, { "width": "3", - "title": "Upstream CX None Healthy Increase", + "title": "Upstream Connection None Healthy Increase", "height": 350, "entityType": "ServiceInstance", "independentSelector": false, "metricType": "LABELED_VALUE", - "metricName": "envoy_cluster_upstream_cx_none_healthy_increase", + "metricName": "envoy_cluster_up_cx_none_healthy_incr", "queryMetricType": "readLabeledMetricsValues", - "unit": "pm", + "unit": "Per Minute", "chartType": "ChartLine" } ] diff --git a/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/topology-service-relation.yml b/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/topology-service-relation.yml index efc5a61d5..ee5e69bea 100644 --- a/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/topology-service-relation.yml +++ b/oap-server/server-bootstrap/src/main/resources/ui-initialized-templates/topology-service-relation.yml @@ -243,6 +243,86 @@ templates: "unit":"PPM - Packets Per Minutes" } ] + }, + "envoy_metrics":{ + "server":[ + ], + "client":[ + { + "width":12, + "title":"Upstream Connection Active", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_up_cx_active", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine" + }, + { + "width":12, + "title":"Upstream Connection Increase", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_up_cx_incr", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine", + "unit":"Per Minute" + }, + { + "width":12, + "title":"Upstream Request Active", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_up_rq_active", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine" + }, + { + "width":12, + "title":"Upstream Request Increase", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_up_rq_incr", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine", + "unit":"Per Minute" + }, + { + "width":12, + "title":"Upstream Request Pending Active", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_up_rq_pending_active", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine" + }, + { + "width":12, + "title":"LB Healthy Panic Increase", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_lb_healthy_panic_incr", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine", + "unit":"Per Minute" + }, + { + "width":12, + "title":"Upstream Connection None Healthy Increase", + "height":"200", + "entityType":"ServiceRelation", + "metricType":"REGULAR_VALUE", + "metricName":"envoy_sr_cluster_up_cx_none_healthy_incr", + "queryMetricType":"readMetricsValues", + "chartType":"ChartLine", + "unit":"Per Minute" + } + ] } } # Activated as the TOPOLOGY_INSTANCE type, makes this templates added into the UI page automatically. diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java index 1fa938c1a..4bacf1550 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/manual/relation/service/ServiceRelationClientSideMetrics.java @@ -68,6 +68,13 @@ public class ServiceRelationClientSideMetrics extends Metrics { @Override public boolean combine(Metrics metrics) { + ServiceRelationClientSideMetrics serviceRelationClientSideMetrics = (ServiceRelationClientSideMetrics) metrics; + if (this.getComponentId() == 0 && serviceRelationClientSideMetrics.getComponentId() != 0) { + this.componentId = serviceRelationClientSideMetrics.getComponentId(); + } + if (this.getTimeBucket() > metrics.getTimeBucket()) { + this.setTimeBucket(metrics.getTimeBucket()); + } return true; } diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterEntity.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterEntity.java index 2647bc73e..d2d595827 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterEntity.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterEntity.java @@ -18,11 +18,14 @@ package org.apache.skywalking.oap.server.core.analysis.meter; +import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.Setter; import lombok.ToString; import org.apache.skywalking.oap.server.core.UnexpectedException; import org.apache.skywalking.oap.server.core.analysis.IDManager; +import org.apache.skywalking.oap.server.core.source.DetectPoint; /** * MeterEntity represents the entity in the meter system. @@ -30,21 +33,16 @@ import org.apache.skywalking.oap.server.core.analysis.IDManager; @EqualsAndHashCode @ToString @Getter +@Builder(toBuilder = true) public class MeterEntity { private ScopeType scopeType; + @Setter private String serviceName; private String instanceName; private String endpointName; - - private MeterEntity(final ScopeType scopeType, - final String serviceName, - final String instanceName, - final String endpointName) { - this.scopeType = scopeType; - this.serviceName = serviceName; - this.instanceName = instanceName; - this.endpointName = endpointName; - } + private String sourceServiceName; + private String destServiceName; + private DetectPoint detectPoint; public String id() { switch (scopeType) { @@ -56,6 +54,11 @@ public class MeterEntity { IDManager.ServiceID.buildId(serviceName, true), instanceName); case ENDPOINT: return IDManager.EndpointID.buildId(IDManager.ServiceID.buildId(serviceName, true), endpointName); + case SERVICE_RELATION: + return IDManager.ServiceID.buildRelationId(new IDManager.ServiceID.ServiceRelationDefine( + sourceServiceId(), + destServiceId() + )); default: throw new UnexpectedException("Unexpected scope type of entity " + this.toString()); } @@ -65,24 +68,51 @@ public class MeterEntity { return IDManager.ServiceID.buildId(serviceName, true); } + public String sourceServiceId() { + return IDManager.ServiceID.buildId(sourceServiceName, true); + } + + public String destServiceId() { + return IDManager.ServiceID.buildId(destServiceName, true); + } + /** * Create a service level meter entity. */ public static MeterEntity newService(String serviceName) { - return new MeterEntity(ScopeType.SERVICE, serviceName, null, null); + return MeterEntity.builder().scopeType(ScopeType.SERVICE).serviceName(serviceName).build(); } /** * Create a service instance level meter entity. */ public static MeterEntity newServiceInstance(String serviceName, String serviceInstance) { - return new MeterEntity(ScopeType.SERVICE_INSTANCE, serviceName, serviceInstance, null); + return MeterEntity.builder() + .scopeType(ScopeType.SERVICE_INSTANCE) + .serviceName(serviceName) + .instanceName(serviceInstance) + .build(); } /** * Create an endpoint level meter entity. */ public static MeterEntity newEndpoint(String serviceName, String endpointName) { - return new MeterEntity(ScopeType.ENDPOINT, serviceName, null, endpointName); + return MeterEntity.builder() + .scopeType(ScopeType.ENDPOINT) + .serviceName(serviceName) + .endpointName(endpointName) + .build(); + } + + public static MeterEntity newServiceRelation(String sourceServiceName, + String destServiceName, + DetectPoint detectPoint) { + return MeterEntity.builder() + .scopeType(ScopeType.SERVICE_RELATION) + .sourceServiceName(sourceServiceName) + .destServiceName(destServiceName) + .detectPoint(detectPoint) + .build(); } } diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/ScopeType.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/ScopeType.java index 9907e56f2..3d0a4a26b 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/ScopeType.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/ScopeType.java @@ -24,7 +24,8 @@ import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine; public enum ScopeType { SERVICE(DefaultScopeDefine.SERVICE), SERVICE_INSTANCE(DefaultScopeDefine.SERVICE_INSTANCE), - ENDPOINT(DefaultScopeDefine.ENDPOINT); + ENDPOINT(DefaultScopeDefine.ENDPOINT), + SERVICE_RELATION(DefaultScopeDefine.SERVICE_RELATION); @Getter private final int scopeId; diff --git a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java index 2f3d130e9..3db3fe02e 100644 --- a/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java +++ b/oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/EnvoyMetricReceiverConfig.java @@ -57,7 +57,7 @@ public class EnvoyMetricReceiverConfig extends ModuleConfig { } public List rules() throws ModuleStartException { - return Rules.loadRules("envoy-metrics-rules", Collections.singletonList("envoy")); + return Rules.loadRules("envoy-metrics-rules", Arrays.asList("envoy", "envoy-svc-relation")); } public ServiceMetaInfoFactory serviceMetaInfoFactory() { diff --git a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java index 73af78fa4..320182b90 100644 --- a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java +++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java @@ -78,7 +78,7 @@ public class MetricsQuery extends AbstractQuery { public static String INSTANCE_JVM_THREAD_RUNNABLE_STATE_THREAD_COUNT = "instance_jvm_thread_runnable_state_thread_count"; public static String INSTANCE_JVM_CLASS_LOADED_CLASS_COUNT = "instance_jvm_class_loaded_class_count"; public static String INSTANCE_JVM_CLASS_TOTAL_LOADED_CLASS_COUNT = "instance_jvm_class_total_loaded_class_count"; - public static String [] ALL_INSTANCE_JVM_METRICS = { + public static String[] ALL_INSTANCE_JVM_METRICS = { INSTANCE_JVM_CLASS_TOTAL_LOADED_CLASS_COUNT, INSTANCE_JVM_CLASS_LOADED_CLASS_COUNT, INSTANCE_JVM_THREAD_RUNNABLE_STATE_THREAD_COUNT, @@ -88,16 +88,16 @@ public class MetricsQuery extends AbstractQuery { INSTANCE_JVM_MEMORY_NOHEAP, INSTANCE_JVM_MEMORY_HEAP_MAX, INSTANCE_JVM_MEMORY_HEAP, - }; + }; - public static String [] ALL_INSTANCE_JVM_METRICS_COMPAT = { + public static String[] ALL_INSTANCE_JVM_METRICS_COMPAT = { INSTANCE_JVM_THREAD_LIVE_COUNT, INSTANCE_JVM_THREAD_DAEMON_COUNT, INSTANCE_JVM_THREAD_PEAK_COUNT, INSTANCE_JVM_MEMORY_NOHEAP, INSTANCE_JVM_MEMORY_HEAP_MAX, INSTANCE_JVM_MEMORY_HEAP, - }; + }; public static String SERVICE_RELATION_CLIENT_CPM = "service_relation_client_cpm"; public static String SERVICE_RELATION_SERVER_CPM = "service_relation_server_cpm"; @@ -110,6 +110,9 @@ public class MetricsQuery extends AbstractQuery { public static String[] ALL_SERVICE_RELATION_CLIENT_METRICS = { SERVICE_RELATION_CLIENT_CPM }; + public static String[] ENVOY_METRICS_SERVICE_RELATION_CLIENT_METRICS = { + "envoy_sr_cluster_up_cx_active" + }; public static String[] ALL_SERVICE_RELATION_SERVER_METRICS = { SERVICE_RELATION_SERVER_CPM @@ -161,11 +164,18 @@ public class MetricsQuery extends AbstractQuery { "envoy_memory_allocated_max", "envoy_memory_physical_size", "envoy_memory_physical_size_max", - "envoy_total_connections_used", "envoy_worker_threads", "envoy_worker_threads_max" }; + public static Map> ALL_ENVOY_LABELED_METRICS = + ImmutableMap.>builder() + .put( + "envoy_cluster_membership_healthy", + Arrays.asList("e2e::details") + ) + .build(); + public static String[] ALL_SO11Y_LABELED_METRICS = { // Nothing to check for now. }; @@ -182,10 +192,19 @@ public class MetricsQuery extends AbstractQuery { }; public static Map> SIMPLE_ZABBIX_METERS = ImmutableMap.>builder() - .put("meter_agent_system_cpu_util", Arrays.asList("idle")) - .put("meter_agent_vm_memory_size", Arrays.asList("total")) - .put("meter_agent_vfs_fs_size", Arrays.asList("/-total")) - .build(); + .put( + "meter_agent_system_cpu_util", + Arrays.asList("idle") + ) + .put( + "meter_agent_vm_memory_size", + Arrays.asList("total") + ) + .put( + "meter_agent_vfs_fs_size", + Arrays.asList("/-total") + ) + .build(); public static String[] SIMPLE_PROM_VM_METERS = { "meter_vm_memory_used", @@ -201,8 +220,14 @@ public class MetricsQuery extends AbstractQuery { }; public static Map> SIMPLE_PROM_VM_LABELED_METERS = ImmutableMap.>builder() - .put("meter_vm_cpu_average_used", Arrays.asList("idle")) - .put("meter_vm_filesystem_percentage", Arrays.asList("/etc/hosts")) - .build(); + .put( + "meter_vm_cpu_average_used", + Arrays.asList("idle") + ) + .put( + "meter_vm_filesystem_percentage", + Arrays.asList("/etc/hosts") + ) + .build(); } diff --git a/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/mesh/MetricsServiceE2E.java b/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/mesh/MetricsServiceE2E.java index 0d87e9be3..f758896c8 100644 --- a/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/mesh/MetricsServiceE2E.java +++ b/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/mesh/MetricsServiceE2E.java @@ -21,6 +21,7 @@ package org.apache.skywalking.e2e.mesh; import com.google.common.base.Strings; import java.net.URL; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -28,7 +29,9 @@ import org.apache.skywalking.e2e.base.SkyWalkingTestAdapter; import org.apache.skywalking.e2e.base.TrafficController; import org.apache.skywalking.e2e.common.HostAndPort; import org.apache.skywalking.e2e.metrics.AtLeastOneOfMetricsMatcher; +import org.apache.skywalking.e2e.metrics.Metrics; import org.apache.skywalking.e2e.metrics.MetricsValueMatcher; +import org.apache.skywalking.e2e.metrics.ReadLabeledMetricsQuery; import org.apache.skywalking.e2e.metrics.ReadMetrics; import org.apache.skywalking.e2e.metrics.ReadMetricsQuery; import org.apache.skywalking.e2e.retryable.RetryableTest; @@ -39,10 +42,17 @@ import org.apache.skywalking.e2e.service.instance.Instance; import org.apache.skywalking.e2e.service.instance.Instances; import org.apache.skywalking.e2e.service.instance.InstancesMatcher; import org.apache.skywalking.e2e.service.instance.InstancesQuery; +import org.apache.skywalking.e2e.topo.Call; +import org.apache.skywalking.e2e.topo.TopoMatcher; +import org.apache.skywalking.e2e.topo.TopoQuery; +import org.apache.skywalking.e2e.topo.Topology; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInstance; +import static org.apache.skywalking.e2e.metrics.MetricsMatcher.verifyMetrics; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_ENVOY_LABELED_METRICS; import static org.apache.skywalking.e2e.metrics.MetricsQuery.ALL_ENVOY_LINER_METRICS; +import static org.apache.skywalking.e2e.metrics.MetricsQuery.ENVOY_METRICS_SERVICE_RELATION_CLIENT_METRICS; import static org.apache.skywalking.e2e.utils.Times.now; import static org.apache.skywalking.e2e.utils.Yamls.exists; import static org.apache.skywalking.e2e.utils.Yamls.load; @@ -50,9 +60,11 @@ import static org.apache.skywalking.e2e.utils.Yamls.load; @Slf4j @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class MetricsServiceE2E extends SkyWalkingTestAdapter { - private final String swWebappHost = Optional.ofNullable(Strings.emptyToNull(System.getenv("WEBAPP_HOST"))).orElse("127.0.0.1"); + private final String swWebappHost = Optional.ofNullable(Strings.emptyToNull(System.getenv("WEBAPP_HOST"))) + .orElse("127.0.0.1"); - private final String swWebappPort = Optional.ofNullable(Strings.emptyToNull(System.getenv("WEBAPP_PORT"))).orElse("12800"); + private final String swWebappPort = Optional.ofNullable(Strings.emptyToNull(System.getenv("WEBAPP_PORT"))) + .orElse("12800"); protected HostAndPort swWebappHostPort = HostAndPort.builder() .host(swWebappHost) @@ -65,8 +77,10 @@ public class MetricsServiceE2E extends SkyWalkingTestAdapter { queryClient(swWebappHostPort); - String gatewayHost = Strings.isNullOrEmpty(System.getenv("GATEWAY_HOST")) ? "127.0.0.1" : System.getenv("GATEWAY_HOST"); - String gatewayPort = Strings.isNullOrEmpty(System.getenv("GATEWAY_PORT")) ? "80" : System.getenv("GATEWAY_PORT"); + String gatewayHost = Strings.isNullOrEmpty(System.getenv("GATEWAY_HOST")) ? "127.0.0.1" : System.getenv( + "GATEWAY_HOST"); + String gatewayPort = Strings.isNullOrEmpty(System.getenv("GATEWAY_PORT")) ? "80" : System.getenv( + "GATEWAY_PORT"); HostAndPort serviceHostPort = HostAndPort.builder() .host(gatewayHost) @@ -124,6 +138,59 @@ public class MetricsServiceE2E extends SkyWalkingTestAdapter { instanceRespTimeMatcher.setValue(greaterThanZero); instanceRespTimeMatcher.verify(instanceMetrics.getValues()); } + + for (Map.Entry> entry : ALL_ENVOY_LABELED_METRICS.entrySet()) { + String metricsName = entry.getKey(); + List labels = entry.getValue(); + LOGGER.info("verifying envoy labeledMetrics: {}", metricsName); + List labeledMetrics = graphql.readLabeledMetrics( + new ReadLabeledMetricsQuery().stepByMinute() + .metricsName(metricsName) + .serviceName(service.getLabel()) + .scope("ServiceInstance") + .instanceName(instance.getLabel()) + .labels(labels) + ); + LOGGER.info("envoy labeledMetrics: {}", labeledMetrics); + + Metrics allValues = new Metrics(); + for (ReadMetrics readMetrics : labeledMetrics) { + allValues.getValues().addAll(readMetrics.getValues().getValues()); + } + final AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher(); + final MetricsValueMatcher greaterThanZero = new MetricsValueMatcher(); + greaterThanZero.setValue("gt 0"); + instanceRespTimeMatcher.setValue(greaterThanZero); + instanceRespTimeMatcher.verify(allValues); + } + } + } + } + + @RetryableTest + void topology() throws Exception { + LOGGER.info("topology starts {} {}", graphql, startTime); + + final Topology topology = graphql.topo(new TopoQuery().stepByMinute().start(startTime.minusDays(1)).end(now())); + + LOGGER.info("topology: {}", topology); + + load("expected/metricsservice/topo.yml").as(TopoMatcher.class).verify(topology); + + verifyServiceRelationMetrics(topology.getCalls()); + } + + private void verifyServiceRelationMetrics(final List calls) throws Exception { + verifyRelationMetrics(calls, ENVOY_METRICS_SERVICE_RELATION_CLIENT_METRICS); + } + + private void verifyRelationMetrics(final List calls, + final String[] relationClientMetrics) throws Exception { + for (Call call : calls) { + for (String detectPoint : call.getDetectPoints()) { + for (String metricName : relationClientMetrics) { + verifyMetrics(graphql, metricName, call.getId(), startTime); + } } } } diff --git a/test/e2e/e2e-test/src/test/resources/expected/metricsservice/topo.yml b/test/e2e/e2e-test/src/test/resources/expected/metricsservice/topo.yml new file mode 100644 index 000000000..beb5c951a --- /dev/null +++ b/test/e2e/e2e-test/src/test/resources/expected/metricsservice/topo.yml @@ -0,0 +1,39 @@ +# 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. + +nodes: + - id: not null + name: e2e::reviews + type: null + isReal: true + - id: not null + name: e2e::productpage + isReal: true + - id: not null + name: e2e::details + type: null + isReal: true + +calls: + - id: not null + source: ZTJlOjpwcm9kdWN0cGFnZQ==.1 + target: ZTJlOjpyZXZpZXdz.1 + detectPoints: + - CLIENT + - id: not null + source: ZTJlOjpwcm9kdWN0cGFnZQ==.1 + target: ZTJlOjpkZXRhaWxz.1 + detectPoints: + - CLIENT