From e099da24730c5db34119f0075380ca03e0953774 Mon Sep 17 00:00:00 2001 From: cleverchuk Date: Mon, 15 Dec 2025 17:43:50 -0500 Subject: [PATCH 1/2] use key presence for stacktrace filter instead of value --- .../extensions/SpanStacktraceFilter.java | 24 ++-- .../extensions/SpanStacktraceFilterTest.java | 123 ++++++++++++++++-- 2 files changed, 125 insertions(+), 22 deletions(-) diff --git a/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java b/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java index bd3e5a21..75966891 100644 --- a/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java +++ b/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java @@ -16,29 +16,37 @@ package com.solarwinds.opentelemetry.extensions; -import static io.opentelemetry.api.common.AttributeKey.stringKey; - import com.solarwinds.joboe.config.ConfigManager; import com.solarwinds.joboe.config.ConfigProperty; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.trace.ReadableSpan; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; public class SpanStacktraceFilter implements Predicate { - private static final Set filterAttributes = new HashSet<>(); + private Set filterAttributes = new HashSet<>(); @Override public boolean test(ReadableSpan readableSpan) { if (filterAttributes.isEmpty()) { - filterAttributes.add("db.system"); Set configuredFilterAttributes = ConfigManager.getConfigOptional( - ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, filterAttributes); - filterAttributes.addAll(configuredFilterAttributes); + ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, Collections.singleton("db.system")); + + if (configuredFilterAttributes.size() > 1) { + configuredFilterAttributes.add("db.system"); + } + filterAttributes = configuredFilterAttributes; } - return filterAttributes.stream() - .anyMatch(attr -> readableSpan.getAttribute(stringKey(attr)) != null); + Set attributes = + readableSpan.getAttributes().asMap().keySet().stream() + .map(AttributeKey::getKey) + .collect(Collectors.toSet()); + attributes.retainAll(filterAttributes); + return !attributes.isEmpty(); } } diff --git a/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java b/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java index 80628c95..37ed5643 100644 --- a/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java +++ b/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java @@ -16,32 +16,127 @@ package com.solarwinds.opentelemetry.extensions; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.solarwinds.joboe.config.ConfigManager; +import com.solarwinds.joboe.config.ConfigProperty; +import com.solarwinds.joboe.config.InvalidConfigException; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.trace.ReadableSpan; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -@ExtendWith(MockitoExtension.class) class SpanStacktraceFilterTest { - @InjectMocks private SpanStacktraceFilter tested; + private SpanStacktraceFilter tested; - @Mock private ReadableSpan readableSpanMock; + @BeforeEach + void setUp() { + tested = new SpanStacktraceFilter(); + } + + @AfterEach + void tearDown() { + ConfigManager.reset(); + } + + @Test + void returnTrueWhenSpanHasMultipleMatchingAttributes() { + ReadableSpan span = + createSpanWithAttributes( + Attributes.builder().put("db.system", "postgresql").put("http.method", "GET").build()); + assertTrue(tested.test(span)); + } + + @Test + void returnFalseWhenSpanHasNoAttributes() { + ReadableSpan span = createSpanWithAttributes(Attributes.empty()); + assertFalse(tested.test(span)); + } + + @Test + void returnTrueWhenSpanHasCustomConfiguredLongAttribute() throws InvalidConfigException { + Set customFilters = new HashSet<>(); + customFilters.add("process.id"); + ConfigManager.setConfig(ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, customFilters); + + SpanStacktraceFilter filter = new SpanStacktraceFilter(); + ReadableSpan span = + createSpanWithAttributes(Attributes.builder().put("process.id", 450).build()); + + assertTrue(filter.test(span)); + } @Test - void returnTrueWhenAttributeHasValue() { - when(readableSpanMock.getAttribute(any())).thenReturn("test-value"); - assertTrue(tested.test(readableSpanMock)); + void returnTrueWhenSpanHasCustomConfiguredStringArrayAttribute() throws InvalidConfigException { + Set customFilters = new HashSet<>(); + customFilters.add("messaging.systems"); + ConfigManager.setConfig(ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, customFilters); + + SpanStacktraceFilter filter = new SpanStacktraceFilter(); + ReadableSpan spanWithMessaging = + createSpanWithAttributes( + Attributes.builder() + .put( + AttributeKey.stringArrayKey("messaging.systems"), + Arrays.asList("postgresql", "rabbitmq")) + .build()); + + assertTrue(filter.test(spanWithMessaging)); } @Test - void returnFalseWhenAttributeHasNoValue() { - assertFalse(tested.test(readableSpanMock)); + void returnTrueWhenSpanHasCustomConfiguredLongArrayAttribute() throws InvalidConfigException { + Set customFilters = new HashSet<>(); + customFilters.add("process.ids"); + ConfigManager.setConfig(ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, customFilters); + + SpanStacktraceFilter filter = new SpanStacktraceFilter(); + ReadableSpan span = + createSpanWithAttributes( + Attributes.builder() + .put(AttributeKey.longArrayKey("process.ids"), Arrays.asList(200L, 300L)) + .build()); + + assertTrue(filter.test(span)); + } + + @Test + void verifyThatConfiguredAttributeIsUnchangedAcrossInvocation() throws InvalidConfigException { + Set customFilters = new HashSet<>(); + customFilters.add("process.ids"); + customFilters.add("process.id"); + ConfigManager.setConfig(ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, customFilters); + + SpanStacktraceFilter filter = new SpanStacktraceFilter(); + ReadableSpan span0 = + createSpanWithAttributes( + Attributes.builder() + .put(AttributeKey.longArrayKey("process.ids"), Arrays.asList(200L, 300L)) + .put("process.id", 300L) + .build()); + + ReadableSpan span1 = + createSpanWithAttributes(Attributes.builder().put("process.id", 300L).build()); + ReadableSpan span2 = + createSpanWithAttributes(Attributes.builder().put("process.name", "test-1").build()); + + assertTrue(filter.test(span0)); + assertTrue(filter.test(span1)); + assertFalse(filter.test(span2)); + } + + private ReadableSpan createSpanWithAttributes(Attributes attributes) { + ReadableSpan span = mock(ReadableSpan.class); + when(span.getAttributes()).thenReturn(attributes); + return span; } } From 317dddf988e15cd218b1823f9a02470e561f3e4a Mon Sep 17 00:00:00 2001 From: cleverchuk Date: Wed, 17 Dec 2025 10:47:55 -0500 Subject: [PATCH 2/2] optimize --- .../extensions/SpanStacktraceFilter.java | 11 ++++---- .../extensions/SpanStacktraceFilterTest.java | 25 ------------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java b/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java index 75966891..b2413620 100644 --- a/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java +++ b/custom/shared/src/main/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilter.java @@ -36,17 +36,16 @@ public boolean test(ReadableSpan readableSpan) { ConfigManager.getConfigOptional( ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, Collections.singleton("db.system")); - if (configuredFilterAttributes.size() > 1) { - configuredFilterAttributes.add("db.system"); - } - filterAttributes = configuredFilterAttributes; + Set effectiveFilterAttributes = new HashSet<>(configuredFilterAttributes); + effectiveFilterAttributes.add("db.system"); + filterAttributes = effectiveFilterAttributes; } Set attributes = readableSpan.getAttributes().asMap().keySet().stream() .map(AttributeKey::getKey) .collect(Collectors.toSet()); - attributes.retainAll(filterAttributes); - return !attributes.isEmpty(); + + return filterAttributes.stream().anyMatch(attributes::contains); } } diff --git a/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java b/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java index 37ed5643..b51760b6 100644 --- a/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java +++ b/custom/shared/src/test/java/com/solarwinds/opentelemetry/extensions/SpanStacktraceFilterTest.java @@ -109,31 +109,6 @@ void returnTrueWhenSpanHasCustomConfiguredLongArrayAttribute() throws InvalidCon assertTrue(filter.test(span)); } - @Test - void verifyThatConfiguredAttributeIsUnchangedAcrossInvocation() throws InvalidConfigException { - Set customFilters = new HashSet<>(); - customFilters.add("process.ids"); - customFilters.add("process.id"); - ConfigManager.setConfig(ConfigProperty.AGENT_SPAN_STACKTRACE_FILTERS, customFilters); - - SpanStacktraceFilter filter = new SpanStacktraceFilter(); - ReadableSpan span0 = - createSpanWithAttributes( - Attributes.builder() - .put(AttributeKey.longArrayKey("process.ids"), Arrays.asList(200L, 300L)) - .put("process.id", 300L) - .build()); - - ReadableSpan span1 = - createSpanWithAttributes(Attributes.builder().put("process.id", 300L).build()); - ReadableSpan span2 = - createSpanWithAttributes(Attributes.builder().put("process.name", "test-1").build()); - - assertTrue(filter.test(span0)); - assertTrue(filter.test(span1)); - assertFalse(filter.test(span2)); - } - private ReadableSpan createSpanWithAttributes(Attributes attributes) { ReadableSpan span = mock(ReadableSpan.class); when(span.getAttributes()).thenReturn(attributes);