From c140246575072dd92918592d511d3979b3ca49f5 Mon Sep 17 00:00:00 2001 From: avinash Date: Thu, 22 Jul 2021 22:02:25 +0530 Subject: [PATCH 1/6] Add support for filtering root spans matching a criteria --- .../jaeger/JaegerSpanPreProcessor.java | 33 +++++++++++++++---- .../spannormalizer/jaeger/SpanFilter.java | 14 +++----- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java index adf65c755..50d585e55 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java @@ -1,12 +1,15 @@ package org.hypertrace.core.spannormalizer.jaeger; import static org.hypertrace.core.spannormalizer.constants.SpanNormalizerConstants.SPAN_NORMALIZER_JOB_CONFIG; +import static org.hypertrace.core.spannormalizer.jaeger.SpanFilter.ROOT_SPAN_DROP_CRITERION_CONFIG; +import static org.hypertrace.core.spannormalizer.jaeger.SpanFilter.SPAN_DROP_CRITERION_CONFIG; import com.google.common.annotations.VisibleForTesting; import com.typesafe.config.Config; import io.jaegertracing.api_v2.JaegerSpanInternalModel; import io.jaegertracing.api_v2.JaegerSpanInternalModel.Span; import io.micrometer.core.instrument.Counter; +import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -22,14 +25,13 @@ public class JaegerSpanPreProcessor implements Transformer> { + static final String SPANS_COUNTER = "hypertrace.reported.spans"; private static final Logger LOG = LoggerFactory.getLogger(JaegerSpanPreProcessor.class); - private static final ConcurrentMap statusToSpansCounter = new ConcurrentHashMap<>(); - static final String SPANS_COUNTER = "hypertrace.reported.spans"; - private TenantIdHandler tenantIdHandler; private SpanFilter spanFilter; + private SpanFilter rootSpanFilter; public JaegerSpanPreProcessor() { // empty constructor @@ -38,14 +40,32 @@ public JaegerSpanPreProcessor() { // constructor for testing JaegerSpanPreProcessor(Config jobConfig) { tenantIdHandler = new TenantIdHandler(jobConfig); - spanFilter = new SpanFilter(jobConfig); + spanFilter = + new SpanFilter( + jobConfig.hasPath(SPAN_DROP_CRITERION_CONFIG) + ? jobConfig.getStringList(SPAN_DROP_CRITERION_CONFIG) + : Collections.emptyList()); + rootSpanFilter = + new SpanFilter( + jobConfig.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG) + ? jobConfig.getStringList(ROOT_SPAN_DROP_CRITERION_CONFIG) + : Collections.emptyList()); } @Override public void init(ProcessorContext context) { Config jobConfig = (Config) context.appConfigs().get(SPAN_NORMALIZER_JOB_CONFIG); tenantIdHandler = new TenantIdHandler(jobConfig); - spanFilter = new SpanFilter(jobConfig); + spanFilter = + new SpanFilter( + jobConfig.hasPath(SPAN_DROP_CRITERION_CONFIG) + ? jobConfig.getStringList(SPAN_DROP_CRITERION_CONFIG) + : Collections.emptyList()); + rootSpanFilter = + new SpanFilter( + jobConfig.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG) + ? jobConfig.getStringList(ROOT_SPAN_DROP_CRITERION_CONFIG) + : Collections.emptyList()); } @Override @@ -95,7 +115,8 @@ PreProcessedSpan preProcessSpan(Span value) { String tenantId = maybeTenantId.get(); - if (spanFilter.shouldDropSpan(tags)) { + if (spanFilter.shouldDropSpan(tags) + || (value.getReferencesList().isEmpty() && rootSpanFilter.shouldDropSpan(tags))) { return null; } diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java index d6490e1e7..b6e348d6a 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java @@ -1,9 +1,7 @@ package org.hypertrace.core.spannormalizer.jaeger; -import com.typesafe.config.Config; import io.jaegertracing.api_v2.JaegerSpanInternalModel.KeyValue; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -26,20 +24,16 @@ public class SpanFilter { * ["messaging.destination_kind:queue,messaging.operation:receive,messaging.system:jms"] drops all * spans which have all 3 attribute:value pairs. */ - private static final String SPAN_DROP_CRITERION_CONFIG = "processor.spanDropCriterion"; + public static final String SPAN_DROP_CRITERION_CONFIG = "processor.spanDropCriterion"; + + public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = "processor.rootSpanDropCriterion"; private static final String COMMA = ","; private static final String COLON = ":"; private final List>> spanDropCriterion; - public SpanFilter(Config config) { - - List criterion = - config.hasPath(SPAN_DROP_CRITERION_CONFIG) - ? config.getStringList(SPAN_DROP_CRITERION_CONFIG) - : Collections.emptyList(); - + public SpanFilter(List criterion) { // Parse the config to see if there is any criteria to drop spans. this.spanDropCriterion = criterion.stream() From 484e30cc3c79498dec9979fdb1e3d7bf0a8c67c5 Mon Sep 17 00:00:00 2001 From: avinash Date: Thu, 22 Jul 2021 22:04:02 +0530 Subject: [PATCH 2/6] helm changes --- span-normalizer/helm/templates/span-normalizer-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/span-normalizer/helm/templates/span-normalizer-config.yaml b/span-normalizer/helm/templates/span-normalizer-config.yaml index 888fcb1b7..d6465b1af 100644 --- a/span-normalizer/helm/templates/span-normalizer-config.yaml +++ b/span-normalizer/helm/templates/span-normalizer-config.yaml @@ -56,6 +56,10 @@ data: {{- if hasKey .Values.spanNormalizerConfig.processor "spanDropCriterion" }} spanDropCriterion = {{ .Values.spanNormalizerConfig.processor.spanDropCriterion | toJson }} {{- end }} + + {{- if hasKey .Values.spanNormalizerConfig.processor "rootSpanDropCriterion" }} + rootSpanDropCriterion = {{ .Values.spanNormalizerConfig.processor.rootSpanDropCriterion | toJson }} + {{- end }} } {{- end }} From c198b08905c0879a9b886a5eb961fb05446d327e Mon Sep 17 00:00:00 2001 From: avinash Date: Fri, 23 Jul 2021 11:45:27 +0530 Subject: [PATCH 3/6] Add support for dropping/processing root exit spans with exclusion criteria --- .../jaeger/JaegerSpanPreProcessor.java | 42 ++---- .../jaeger/RootExitSpanFilter.java | 127 ++++++++++++++++++ .../spannormalizer/jaeger/SpanFilter.java | 12 +- 3 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java index 50d585e55..ada9addf9 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java @@ -1,15 +1,12 @@ package org.hypertrace.core.spannormalizer.jaeger; import static org.hypertrace.core.spannormalizer.constants.SpanNormalizerConstants.SPAN_NORMALIZER_JOB_CONFIG; -import static org.hypertrace.core.spannormalizer.jaeger.SpanFilter.ROOT_SPAN_DROP_CRITERION_CONFIG; -import static org.hypertrace.core.spannormalizer.jaeger.SpanFilter.SPAN_DROP_CRITERION_CONFIG; import com.google.common.annotations.VisibleForTesting; import com.typesafe.config.Config; import io.jaegertracing.api_v2.JaegerSpanInternalModel; import io.jaegertracing.api_v2.JaegerSpanInternalModel.Span; import io.micrometer.core.instrument.Counter; -import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -31,7 +28,7 @@ public class JaegerSpanPreProcessor new ConcurrentHashMap<>(); private TenantIdHandler tenantIdHandler; private SpanFilter spanFilter; - private SpanFilter rootSpanFilter; + private RootExitSpanFilter rootSpanFilter; public JaegerSpanPreProcessor() { // empty constructor @@ -40,32 +37,16 @@ public JaegerSpanPreProcessor() { // constructor for testing JaegerSpanPreProcessor(Config jobConfig) { tenantIdHandler = new TenantIdHandler(jobConfig); - spanFilter = - new SpanFilter( - jobConfig.hasPath(SPAN_DROP_CRITERION_CONFIG) - ? jobConfig.getStringList(SPAN_DROP_CRITERION_CONFIG) - : Collections.emptyList()); - rootSpanFilter = - new SpanFilter( - jobConfig.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG) - ? jobConfig.getStringList(ROOT_SPAN_DROP_CRITERION_CONFIG) - : Collections.emptyList()); + spanFilter = new SpanFilter(jobConfig); + rootSpanFilter = new RootExitSpanFilter(jobConfig); } @Override public void init(ProcessorContext context) { Config jobConfig = (Config) context.appConfigs().get(SPAN_NORMALIZER_JOB_CONFIG); tenantIdHandler = new TenantIdHandler(jobConfig); - spanFilter = - new SpanFilter( - jobConfig.hasPath(SPAN_DROP_CRITERION_CONFIG) - ? jobConfig.getStringList(SPAN_DROP_CRITERION_CONFIG) - : Collections.emptyList()); - rootSpanFilter = - new SpanFilter( - jobConfig.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG) - ? jobConfig.getStringList(ROOT_SPAN_DROP_CRITERION_CONFIG) - : Collections.emptyList()); + spanFilter = new SpanFilter(jobConfig); + rootSpanFilter = new RootExitSpanFilter(jobConfig); } @Override @@ -103,12 +84,12 @@ public KeyValue transform(byte[] key, Span value) { } @VisibleForTesting - PreProcessedSpan preProcessSpan(Span value) { + PreProcessedSpan preProcessSpan(Span span) { Map tags = - value.getTagsList().stream() + span.getTagsList().stream() .collect(Collectors.toMap(t -> t.getKey().toLowerCase(), t -> t, (v1, v2) -> v2)); - Optional maybeTenantId = tenantIdHandler.getAllowedTenantId(value, tags); + Optional maybeTenantId = tenantIdHandler.getAllowedTenantId(span, tags); if (maybeTenantId.isEmpty()) { return null; } @@ -116,11 +97,14 @@ PreProcessedSpan preProcessSpan(Span value) { String tenantId = maybeTenantId.get(); if (spanFilter.shouldDropSpan(tags) - || (value.getReferencesList().isEmpty() && rootSpanFilter.shouldDropSpan(tags))) { + || (span.getReferencesList().isEmpty() + && tags.containsKey("span.kind") + && "client".equals(tags.get("span.kind").getVStr()) + && rootSpanFilter.shouldDropSpan(span, tags))) { return null; } - return new PreProcessedSpan(tenantId, value); + return new PreProcessedSpan(tenantId, span); } @Override diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java new file mode 100644 index 000000000..85138adb4 --- /dev/null +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java @@ -0,0 +1,127 @@ +package org.hypertrace.core.spannormalizer.jaeger; + +import com.typesafe.config.Config; +import io.jaegertracing.api_v2.JaegerSpanInternalModel; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.hypertrace.core.span.constants.RawSpanConstants; +import org.hypertrace.core.span.constants.v1.SpanAttribute; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RootExitSpanFilter { + /** + * Config key using which a list of criterion can be specified to drop the matching spans. Any + * span matching any one of the criterion is dropped. Each criteria is a comma separated list of + * key:value pairs and multiple pairs in one criteria are AND'ed. + * + *

For example: + * ["messaging.destination_kind:queue,messaging.operation:receive,messaging.system:jms"] drops all + * spans which have all 3 attribute:value pairs. + */ + public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = + "processor.rootExitSpanDropCriterion"; + + public static final String DEFAULT_OPERATION = "defaultOperation"; + private static final Logger LOG = LoggerFactory.getLogger(SpanFilter.class); + private static final String DROP = "drop"; + private static final String PROCESS = "process"; + private static final String EXCLUSIONS = "exclusionsMatchCriterion"; + + private static final String COMMA = ","; + private static final String COLON = ":"; + + private static final String SPAN_KIND_TAG = + RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND); + private static final String SPAN_KIND_CLIENT = "client"; + + private String defaultOperation = PROCESS; + private List>> exclusionsMatchCriterion = Collections.emptyList(); + + public RootExitSpanFilter(Config config) { + if (config.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG)) { + Config dropCriterionConfig = config.getConfig(ROOT_SPAN_DROP_CRITERION_CONFIG); + LOG.info("Span drop criterion: {}", dropCriterionConfig); + defaultOperation = + dropCriterionConfig.hasPath(DEFAULT_OPERATION) + ? dropCriterionConfig.getString(DEFAULT_OPERATION) + : PROCESS; + List exclusionList = + dropCriterionConfig.hasPath(EXCLUSIONS) + ? dropCriterionConfig.getStringList(EXCLUSIONS) + : Collections.emptyList(); + // Parse the config to see if there is any criteria to drop spans. + this.exclusionsMatchCriterion = + exclusionList.stream() + // Split each criteria based on comma + .map(s -> s.split(COMMA)) + .map( + a -> + Arrays.stream(a) + .map(this::convertToPair) + .filter(Objects::nonNull) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + } + } + + @Nullable + private Pair convertToPair(String s) { + if (s != null && s.contains(COLON)) { + String[] parts = s.split(COLON); + if (parts.length == 2) { + return Pair.of(parts[0], parts[1]); + } + } + return null; + } + + /** + * Method to check if the given span attributes match any of the drop criterion. Returns true if + * the span should be dropped, false otherwise. + */ + public boolean shouldDropSpan( + JaegerSpanInternalModel.Span span, Map tags) { + return isRootExitSpan(span, tags) + && (PROCESS.equals(defaultOperation) + && exclusionsMatchCriterion.stream() + .anyMatch( + l -> + l.stream() + .allMatch( + p -> + tags.containsKey(p.getLeft()) + && StringUtils.equals( + tags.get(p.getLeft()).getVStr(), p.getRight())))) + || (DROP.equals(defaultOperation) + && exclusionsMatchCriterion.stream() + .noneMatch( + l -> + l.stream() + .allMatch( + p -> + tags.containsKey(p.getLeft()) + && StringUtils.equals( + tags.get(p.getLeft()).getVStr(), p.getRight())))); + } + + private boolean isRootExitSpan( + JaegerSpanInternalModel.Span span, Map tags) { + if (!span.getReferencesList().isEmpty()) { + return false; + } + JaegerSpanInternalModel.KeyValue spanKindKeyValue = tags.get(SPAN_KIND_TAG); + if (spanKindKeyValue == null) { + return false; + } + + return SPAN_KIND_CLIENT.equals(spanKindKeyValue.getVStr()); + } +} diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java index b6e348d6a..241b0219e 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java @@ -1,7 +1,9 @@ package org.hypertrace.core.spannormalizer.jaeger; +import com.typesafe.config.Config; import io.jaegertracing.api_v2.JaegerSpanInternalModel.KeyValue; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -24,16 +26,18 @@ public class SpanFilter { * ["messaging.destination_kind:queue,messaging.operation:receive,messaging.system:jms"] drops all * spans which have all 3 attribute:value pairs. */ - public static final String SPAN_DROP_CRITERION_CONFIG = "processor.spanDropCriterion"; - - public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = "processor.rootSpanDropCriterion"; + private static final String SPAN_DROP_CRITERION_CONFIG = "processor.spanDropCriterion"; private static final String COMMA = ","; private static final String COLON = ":"; private final List>> spanDropCriterion; - public SpanFilter(List criterion) { + public SpanFilter(Config config) { + List criterion = + config.hasPath(SPAN_DROP_CRITERION_CONFIG) + ? config.getStringList(SPAN_DROP_CRITERION_CONFIG) + : Collections.emptyList(); // Parse the config to see if there is any criteria to drop spans. this.spanDropCriterion = criterion.stream() From b9ce1aad4d38ac2b5bf0139eb3624d21f5494061 Mon Sep 17 00:00:00 2001 From: avinash Date: Fri, 23 Jul 2021 12:11:31 +0530 Subject: [PATCH 4/6] Moved all span dropping logic to SpanFilter --- .../templates/span-normalizer-config.yaml | 4 +- .../jaeger/JaegerSpanPreProcessor.java | 9 +- .../jaeger/RootExitSpanFilter.java | 127 ------------------ .../spannormalizer/jaeger/SpanFilter.java | 96 ++++++++++++- 4 files changed, 92 insertions(+), 144 deletions(-) delete mode 100644 span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java diff --git a/span-normalizer/helm/templates/span-normalizer-config.yaml b/span-normalizer/helm/templates/span-normalizer-config.yaml index d6465b1af..d5fd1e3f1 100644 --- a/span-normalizer/helm/templates/span-normalizer-config.yaml +++ b/span-normalizer/helm/templates/span-normalizer-config.yaml @@ -57,8 +57,8 @@ data: spanDropCriterion = {{ .Values.spanNormalizerConfig.processor.spanDropCriterion | toJson }} {{- end }} - {{- if hasKey .Values.spanNormalizerConfig.processor "rootSpanDropCriterion" }} - rootSpanDropCriterion = {{ .Values.spanNormalizerConfig.processor.rootSpanDropCriterion | toJson }} + {{- if hasKey .Values.spanNormalizerConfig.processor "rootExitSpanDropCriterion" }} + rootExitSpanDropCriterion = {{ .Values.spanNormalizerConfig.processor.rootExitSpanDropCriterion | toJson }} {{- end }} } {{- end }} diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java index ada9addf9..93b62e73d 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessor.java @@ -28,7 +28,6 @@ public class JaegerSpanPreProcessor new ConcurrentHashMap<>(); private TenantIdHandler tenantIdHandler; private SpanFilter spanFilter; - private RootExitSpanFilter rootSpanFilter; public JaegerSpanPreProcessor() { // empty constructor @@ -38,7 +37,6 @@ public JaegerSpanPreProcessor() { JaegerSpanPreProcessor(Config jobConfig) { tenantIdHandler = new TenantIdHandler(jobConfig); spanFilter = new SpanFilter(jobConfig); - rootSpanFilter = new RootExitSpanFilter(jobConfig); } @Override @@ -46,7 +44,6 @@ public void init(ProcessorContext context) { Config jobConfig = (Config) context.appConfigs().get(SPAN_NORMALIZER_JOB_CONFIG); tenantIdHandler = new TenantIdHandler(jobConfig); spanFilter = new SpanFilter(jobConfig); - rootSpanFilter = new RootExitSpanFilter(jobConfig); } @Override @@ -96,11 +93,7 @@ PreProcessedSpan preProcessSpan(Span span) { String tenantId = maybeTenantId.get(); - if (spanFilter.shouldDropSpan(tags) - || (span.getReferencesList().isEmpty() - && tags.containsKey("span.kind") - && "client".equals(tags.get("span.kind").getVStr()) - && rootSpanFilter.shouldDropSpan(span, tags))) { + if (spanFilter.shouldDropSpan(span, tags)) { return null; } diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java deleted file mode 100644 index 85138adb4..000000000 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/RootExitSpanFilter.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.hypertrace.core.spannormalizer.jaeger; - -import com.typesafe.config.Config; -import io.jaegertracing.api_v2.JaegerSpanInternalModel; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import javax.annotation.Nullable; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.hypertrace.core.span.constants.RawSpanConstants; -import org.hypertrace.core.span.constants.v1.SpanAttribute; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RootExitSpanFilter { - /** - * Config key using which a list of criterion can be specified to drop the matching spans. Any - * span matching any one of the criterion is dropped. Each criteria is a comma separated list of - * key:value pairs and multiple pairs in one criteria are AND'ed. - * - *

For example: - * ["messaging.destination_kind:queue,messaging.operation:receive,messaging.system:jms"] drops all - * spans which have all 3 attribute:value pairs. - */ - public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = - "processor.rootExitSpanDropCriterion"; - - public static final String DEFAULT_OPERATION = "defaultOperation"; - private static final Logger LOG = LoggerFactory.getLogger(SpanFilter.class); - private static final String DROP = "drop"; - private static final String PROCESS = "process"; - private static final String EXCLUSIONS = "exclusionsMatchCriterion"; - - private static final String COMMA = ","; - private static final String COLON = ":"; - - private static final String SPAN_KIND_TAG = - RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND); - private static final String SPAN_KIND_CLIENT = "client"; - - private String defaultOperation = PROCESS; - private List>> exclusionsMatchCriterion = Collections.emptyList(); - - public RootExitSpanFilter(Config config) { - if (config.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG)) { - Config dropCriterionConfig = config.getConfig(ROOT_SPAN_DROP_CRITERION_CONFIG); - LOG.info("Span drop criterion: {}", dropCriterionConfig); - defaultOperation = - dropCriterionConfig.hasPath(DEFAULT_OPERATION) - ? dropCriterionConfig.getString(DEFAULT_OPERATION) - : PROCESS; - List exclusionList = - dropCriterionConfig.hasPath(EXCLUSIONS) - ? dropCriterionConfig.getStringList(EXCLUSIONS) - : Collections.emptyList(); - // Parse the config to see if there is any criteria to drop spans. - this.exclusionsMatchCriterion = - exclusionList.stream() - // Split each criteria based on comma - .map(s -> s.split(COMMA)) - .map( - a -> - Arrays.stream(a) - .map(this::convertToPair) - .filter(Objects::nonNull) - .collect(Collectors.toList())) - .collect(Collectors.toList()); - } - } - - @Nullable - private Pair convertToPair(String s) { - if (s != null && s.contains(COLON)) { - String[] parts = s.split(COLON); - if (parts.length == 2) { - return Pair.of(parts[0], parts[1]); - } - } - return null; - } - - /** - * Method to check if the given span attributes match any of the drop criterion. Returns true if - * the span should be dropped, false otherwise. - */ - public boolean shouldDropSpan( - JaegerSpanInternalModel.Span span, Map tags) { - return isRootExitSpan(span, tags) - && (PROCESS.equals(defaultOperation) - && exclusionsMatchCriterion.stream() - .anyMatch( - l -> - l.stream() - .allMatch( - p -> - tags.containsKey(p.getLeft()) - && StringUtils.equals( - tags.get(p.getLeft()).getVStr(), p.getRight())))) - || (DROP.equals(defaultOperation) - && exclusionsMatchCriterion.stream() - .noneMatch( - l -> - l.stream() - .allMatch( - p -> - tags.containsKey(p.getLeft()) - && StringUtils.equals( - tags.get(p.getLeft()).getVStr(), p.getRight())))); - } - - private boolean isRootExitSpan( - JaegerSpanInternalModel.Span span, Map tags) { - if (!span.getReferencesList().isEmpty()) { - return false; - } - JaegerSpanInternalModel.KeyValue spanKindKeyValue = tags.get(SPAN_KIND_TAG); - if (spanKindKeyValue == null) { - return false; - } - - return SPAN_KIND_CLIENT.equals(spanKindKeyValue.getVStr()); - } -} diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java index 241b0219e..5f92a8eb5 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java @@ -1,7 +1,7 @@ package org.hypertrace.core.spannormalizer.jaeger; import com.typesafe.config.Config; -import io.jaegertracing.api_v2.JaegerSpanInternalModel.KeyValue; +import io.jaegertracing.api_v2.JaegerSpanInternalModel; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -11,12 +11,19 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.hypertrace.core.span.constants.RawSpanConstants; +import org.hypertrace.core.span.constants.v1.SpanAttribute; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SpanFilter { + public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = + "processor.rootExitSpanDropCriterion"; private static final Logger LOG = LoggerFactory.getLogger(SpanFilter.class); + private static final String SPAN_KIND_TAG = + RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND); + private static final String SPAN_KIND_CLIENT = "client"; /** * Config key using which a list of criterion can be specified to drop the matching spans. Any * span matching any one of the criterion is dropped. Each criteria is a comma separated list of @@ -28,10 +35,15 @@ public class SpanFilter { */ private static final String SPAN_DROP_CRITERION_CONFIG = "processor.spanDropCriterion"; + private static final String ROOT_SPAN_ALWAYS_DROP = "alwaysDrop"; + private static final String ROOT_SPAN_DROP_EXCLUSIONS = "exclusionsMatchCriterion"; + private static final String COMMA = ","; private static final String COLON = ":"; private final List>> spanDropCriterion; + private boolean dropRootSpan = false; + private List>> rootSpanDropExclusionCriterion = Collections.emptyList(); public SpanFilter(Config config) { List criterion = @@ -54,6 +66,51 @@ public SpanFilter(Config config) { if (!this.spanDropCriterion.isEmpty()) { LOG.info("Span drop criterion: {}", this.spanDropCriterion); } + + if (config.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG)) { + Config dropCriterionConfig = config.getConfig(ROOT_SPAN_DROP_CRITERION_CONFIG); + LOG.info("Root Span drop criterion: {}", dropCriterionConfig); + this.dropRootSpan = + dropCriterionConfig.hasPath(ROOT_SPAN_ALWAYS_DROP) + && dropCriterionConfig.getBoolean(ROOT_SPAN_ALWAYS_DROP); + List exclusionList = + dropCriterionConfig.hasPath(ROOT_SPAN_DROP_EXCLUSIONS) + ? dropCriterionConfig.getStringList(ROOT_SPAN_DROP_EXCLUSIONS) + : Collections.emptyList(); + // Parse the config to see if there is any criteria to drop spans. + this.rootSpanDropExclusionCriterion = + exclusionList.stream() + // Split each criteria based on comma + .map(s -> s.split(COMMA)) + .map( + a -> + Arrays.stream(a) + .map(this::convertToPair) + .filter(Objects::nonNull) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + } + } + + /** + * Method to check if the given span attributes match any of the drop criterion. Returns true if + * the span should be dropped, false otherwise. + */ + public boolean shouldDropSpan( + JaegerSpanInternalModel.Span span, Map tags) { + if (anyCriteriaMatch(tags, spanDropCriterion)) { + return true; + } + + if (isRootExitSpan(span, tags)) { + if (dropRootSpan && noCriteriaMatch(tags, rootSpanDropExclusionCriterion)) { + return true; + } + if (!dropRootSpan && anyCriteriaMatch(tags, rootSpanDropExclusionCriterion)) { + return true; + } + } + return false; } @Nullable @@ -67,12 +124,10 @@ private Pair convertToPair(String s) { return null; } - /** - * Method to check if the given span attributes match any of the drop criterion. Returns true if - * the span should be dropped, false otherwise. - */ - public boolean shouldDropSpan(Map tags) { - return this.spanDropCriterion.stream() + private boolean anyCriteriaMatch( + Map tags, + List>> criteriaList) { + return criteriaList.stream() .anyMatch( l -> l.stream() @@ -82,4 +137,31 @@ public boolean shouldDropSpan(Map tags) { && StringUtils.equals( tags.get(p.getLeft()).getVStr(), p.getRight()))); } + + private boolean noCriteriaMatch( + Map tags, + List>> criteriaList) { + return criteriaList.stream() + .noneMatch( + l -> + l.stream() + .allMatch( + p -> + tags.containsKey(p.getLeft()) + && StringUtils.equals( + tags.get(p.getLeft()).getVStr(), p.getRight()))); + } + + private boolean isRootExitSpan( + JaegerSpanInternalModel.Span span, Map tags) { + if (!span.getReferencesList().isEmpty()) { + return false; + } + JaegerSpanInternalModel.KeyValue spanKindKeyValue = tags.get(SPAN_KIND_TAG); + if (spanKindKeyValue == null) { + return false; + } + + return SPAN_KIND_CLIENT.equals(spanKindKeyValue.getVStr()); + } } From b5c9ea0b2a1b254e714cc60d857ab8f1b4810339 Mon Sep 17 00:00:00 2001 From: avinash Date: Fri, 23 Jul 2021 12:17:41 +0530 Subject: [PATCH 5/6] Further cleanup of config parsing --- .../spannormalizer/jaeger/SpanFilter.java | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java index 5f92a8eb5..57dcce9cf 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java @@ -17,9 +17,6 @@ import org.slf4j.LoggerFactory; public class SpanFilter { - - public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = - "processor.rootExitSpanDropCriterion"; private static final Logger LOG = LoggerFactory.getLogger(SpanFilter.class); private static final String SPAN_KIND_TAG = RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND); @@ -35,47 +32,45 @@ public class SpanFilter { */ private static final String SPAN_DROP_CRITERION_CONFIG = "processor.spanDropCriterion"; + public static final String ROOT_SPAN_DROP_CRITERION_CONFIG = + "processor.rootExitSpanDropCriterion"; private static final String ROOT_SPAN_ALWAYS_DROP = "alwaysDrop"; private static final String ROOT_SPAN_DROP_EXCLUSIONS = "exclusionsMatchCriterion"; private static final String COMMA = ","; private static final String COLON = ":"; - private final List>> spanDropCriterion; + private List>> spanDropCriterion = Collections.emptyList(); private boolean dropRootSpan = false; private List>> rootSpanDropExclusionCriterion = Collections.emptyList(); public SpanFilter(Config config) { - List criterion = - config.hasPath(SPAN_DROP_CRITERION_CONFIG) - ? config.getStringList(SPAN_DROP_CRITERION_CONFIG) - : Collections.emptyList(); - // Parse the config to see if there is any criteria to drop spans. - this.spanDropCriterion = - criterion.stream() - // Split each criteria based on comma - .map(s -> s.split(COMMA)) - .map( - a -> - Arrays.stream(a) - .map(this::convertToPair) - .filter(Objects::nonNull) - .collect(Collectors.toList())) - .collect(Collectors.toList()); - - if (!this.spanDropCriterion.isEmpty()) { - LOG.info("Span drop criterion: {}", this.spanDropCriterion); + if (config.hasPath(SPAN_DROP_CRITERION_CONFIG)) { + List criterion = config.getStringList(SPAN_DROP_CRITERION_CONFIG); + LOG.info("Span drop criterion: {}", criterion); + // Parse the config to see if there is any criteria to drop spans. + this.spanDropCriterion = + criterion.stream() + // Split each criteria based on comma + .map(s -> s.split(COMMA)) + .map( + a -> + Arrays.stream(a) + .map(this::convertToPair) + .filter(Objects::nonNull) + .collect(Collectors.toList())) + .collect(Collectors.toList()); } if (config.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG)) { - Config dropCriterionConfig = config.getConfig(ROOT_SPAN_DROP_CRITERION_CONFIG); - LOG.info("Root Span drop criterion: {}", dropCriterionConfig); + Config rootSpanDropCriterionConfig = config.getConfig(ROOT_SPAN_DROP_CRITERION_CONFIG); + LOG.info("Root Span drop criterion: {}", rootSpanDropCriterionConfig); this.dropRootSpan = - dropCriterionConfig.hasPath(ROOT_SPAN_ALWAYS_DROP) - && dropCriterionConfig.getBoolean(ROOT_SPAN_ALWAYS_DROP); + rootSpanDropCriterionConfig.hasPath(ROOT_SPAN_ALWAYS_DROP) + && rootSpanDropCriterionConfig.getBoolean(ROOT_SPAN_ALWAYS_DROP); List exclusionList = - dropCriterionConfig.hasPath(ROOT_SPAN_DROP_EXCLUSIONS) - ? dropCriterionConfig.getStringList(ROOT_SPAN_DROP_EXCLUSIONS) + rootSpanDropCriterionConfig.hasPath(ROOT_SPAN_DROP_EXCLUSIONS) + ? rootSpanDropCriterionConfig.getStringList(ROOT_SPAN_DROP_EXCLUSIONS) : Collections.emptyList(); // Parse the config to see if there is any criteria to drop spans. this.rootSpanDropExclusionCriterion = From 0e85b1b9ce69a9c008006afe60f04f8c995b81b6 Mon Sep 17 00:00:00 2001 From: rish691 Date: Fri, 23 Jul 2021 13:29:40 +0530 Subject: [PATCH 6/6] Add test --- .../spannormalizer/jaeger/SpanFilter.java | 65 ++----- .../jaeger/JaegerSpanPreProcessorTest.java | 175 ++++++++++++++++++ 2 files changed, 195 insertions(+), 45 deletions(-) diff --git a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java index 57dcce9cf..ac4dffbef 100644 --- a/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java +++ b/span-normalizer/span-normalizer/src/main/java/org/hypertrace/core/spannormalizer/jaeger/SpanFilter.java @@ -41,7 +41,7 @@ public class SpanFilter { private static final String COLON = ":"; private List>> spanDropCriterion = Collections.emptyList(); - private boolean dropRootSpan = false; + private boolean alwaysDropRootSpan = false; private List>> rootSpanDropExclusionCriterion = Collections.emptyList(); public SpanFilter(Config config) { @@ -49,23 +49,13 @@ public SpanFilter(Config config) { List criterion = config.getStringList(SPAN_DROP_CRITERION_CONFIG); LOG.info("Span drop criterion: {}", criterion); // Parse the config to see if there is any criteria to drop spans. - this.spanDropCriterion = - criterion.stream() - // Split each criteria based on comma - .map(s -> s.split(COMMA)) - .map( - a -> - Arrays.stream(a) - .map(this::convertToPair) - .filter(Objects::nonNull) - .collect(Collectors.toList())) - .collect(Collectors.toList()); + this.spanDropCriterion = parseStringList(criterion); } if (config.hasPath(ROOT_SPAN_DROP_CRITERION_CONFIG)) { Config rootSpanDropCriterionConfig = config.getConfig(ROOT_SPAN_DROP_CRITERION_CONFIG); LOG.info("Root Span drop criterion: {}", rootSpanDropCriterionConfig); - this.dropRootSpan = + this.alwaysDropRootSpan = rootSpanDropCriterionConfig.hasPath(ROOT_SPAN_ALWAYS_DROP) && rootSpanDropCriterionConfig.getBoolean(ROOT_SPAN_ALWAYS_DROP); List exclusionList = @@ -73,20 +63,23 @@ public SpanFilter(Config config) { ? rootSpanDropCriterionConfig.getStringList(ROOT_SPAN_DROP_EXCLUSIONS) : Collections.emptyList(); // Parse the config to see if there is any criteria to drop spans. - this.rootSpanDropExclusionCriterion = - exclusionList.stream() - // Split each criteria based on comma - .map(s -> s.split(COMMA)) - .map( - a -> - Arrays.stream(a) - .map(this::convertToPair) - .filter(Objects::nonNull) - .collect(Collectors.toList())) - .collect(Collectors.toList()); + this.rootSpanDropExclusionCriterion = parseStringList(exclusionList); } } + private List>> parseStringList(List stringList) { + return stringList.stream() + // Split each criteria based on comma + .map(s -> s.split(COMMA)) + .map( + a -> + Arrays.stream(a) + .map(this::convertToPair) + .filter(Objects::nonNull) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + } + /** * Method to check if the given span attributes match any of the drop criterion. Returns true if * the span should be dropped, false otherwise. @@ -98,12 +91,8 @@ public boolean shouldDropSpan( } if (isRootExitSpan(span, tags)) { - if (dropRootSpan && noCriteriaMatch(tags, rootSpanDropExclusionCriterion)) { - return true; - } - if (!dropRootSpan && anyCriteriaMatch(tags, rootSpanDropExclusionCriterion)) { - return true; - } + boolean anyCriteriaMatch = anyCriteriaMatch(tags, rootSpanDropExclusionCriterion); + return (alwaysDropRootSpan && !anyCriteriaMatch) || (!alwaysDropRootSpan && anyCriteriaMatch); } return false; } @@ -133,27 +122,13 @@ private boolean anyCriteriaMatch( tags.get(p.getLeft()).getVStr(), p.getRight()))); } - private boolean noCriteriaMatch( - Map tags, - List>> criteriaList) { - return criteriaList.stream() - .noneMatch( - l -> - l.stream() - .allMatch( - p -> - tags.containsKey(p.getLeft()) - && StringUtils.equals( - tags.get(p.getLeft()).getVStr(), p.getRight()))); - } - private boolean isRootExitSpan( JaegerSpanInternalModel.Span span, Map tags) { if (!span.getReferencesList().isEmpty()) { return false; } JaegerSpanInternalModel.KeyValue spanKindKeyValue = tags.get(SPAN_KIND_TAG); - if (spanKindKeyValue == null) { + if (null == spanKindKeyValue) { return false; } diff --git a/span-normalizer/span-normalizer/src/test/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessorTest.java b/span-normalizer/span-normalizer/src/test/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessorTest.java index 7bc409810..5c56b72d7 100644 --- a/span-normalizer/span-normalizer/src/test/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessorTest.java +++ b/span-normalizer/span-normalizer/src/test/java/org/hypertrace/core/spannormalizer/jaeger/JaegerSpanPreProcessorTest.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Map; import java.util.Random; +import org.hypertrace.core.span.constants.RawSpanConstants; +import org.hypertrace.core.span.constants.v1.SpanAttribute; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -179,6 +181,179 @@ public void testDropSpanWithEmptyCriterion() { Assertions.assertNotNull(preProcessedSpan); } + @Test + public void testDropSpan_RootSpan_EmptyExclusionList() { + String tenantId = "tenant-" + random.nextLong(); + Map configs = new HashMap<>(getCommonConfig()); + configs.putAll( + Map.of( + "processor", + Map.of( + "tenantIdTagKey", "tenant-key", + "rootExitSpanDropCriterion.alwaysDrop", "true"))); + + JaegerSpanPreProcessor jaegerSpanPreProcessor = + new JaegerSpanPreProcessor(ConfigFactory.parseMap(configs)); + Process process = Process.newBuilder().setServiceName("testService").build(); + + // root exit span + Span span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("foo").setVStr("bar").build()) + .addTags(KeyValue.newBuilder().setKey("k2").setVStr("v2").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + PreProcessedSpan preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNull(preProcessedSpan); + + span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("foo").setVStr("bar").build()) + .addTags(KeyValue.newBuilder().setKey("k2").setVStr("v2").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("server") + .build()) + .build(); + preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNotNull(preProcessedSpan); + } + + /** Always drop except when there is a match from the exclusion list */ + @Test + public void testDropSpan_RootSpan_AlwaysDrop_ExclusionList() { + String tenantId = "tenant-" + random.nextLong(); + Map configs = new HashMap<>(getCommonConfig()); + configs.putAll( + Map.of( + "processor", + Map.of( + "tenantIdTagKey", "tenant-key", + "rootExitSpanDropCriterion.alwaysDrop", "true", + "rootExitSpanDropCriterion.exclusionsMatchCriterion", + List.of("foo:bar,k1:v1", "k2:v2")))); + + JaegerSpanPreProcessor jaegerSpanPreProcessor = + new JaegerSpanPreProcessor(ConfigFactory.parseMap(configs)); + Process process = Process.newBuilder().setServiceName("testService").build(); + + // root exit span + Span span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("foo").setVStr("bar").build()) + .addTags(KeyValue.newBuilder().setKey("k1").setVStr("v1").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + PreProcessedSpan preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNotNull(preProcessedSpan); + + span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("k2").setVStr("v2").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNotNull(preProcessedSpan); + + span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("k3").setVStr("v3").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNull(preProcessedSpan); + } + + /** Always keep except when there is a match from the exclusion list */ + @Test + public void testDropSpan_RootSpan_NotAlwaysDrop_ExclusionList() { + String tenantId = "tenant-" + random.nextLong(); + Map configs = new HashMap<>(getCommonConfig()); + configs.putAll( + Map.of( + "processor", + Map.of( + "tenantIdTagKey", "tenant-key", + "rootExitSpanDropCriterion.alwaysDrop", "false", + "rootExitSpanDropCriterion.exclusionsMatchCriterion", + List.of("foo:bar,k1:v1", "k2:v2")))); + + JaegerSpanPreProcessor jaegerSpanPreProcessor = + new JaegerSpanPreProcessor(ConfigFactory.parseMap(configs)); + Process process = Process.newBuilder().setServiceName("testService").build(); + + // root exit span + Span span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("foo").setVStr("bar").build()) + .addTags(KeyValue.newBuilder().setKey("k1").setVStr("v1").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + PreProcessedSpan preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNull(preProcessedSpan); + + span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("k2").setVStr("v2").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNull(preProcessedSpan); + + span = + Span.newBuilder() + .setProcess(process) + .addTags(KeyValue.newBuilder().setKey("tenant-key").setVStr(tenantId).build()) + .addTags(KeyValue.newBuilder().setKey("k3").setVStr("v3").build()) + .addTags( + KeyValue.newBuilder() + .setKey(RawSpanConstants.getValue(SpanAttribute.SPAN_ATTRIBUTE_SPAN_KIND)) + .setVStr("client") + .build()) + .build(); + preProcessedSpan = jaegerSpanPreProcessor.preProcessSpan(span); + Assertions.assertNotNull(preProcessedSpan); + } + private Map getCommonConfig() { return Map.of( "span.type",