diff --git a/span-stacktrace/build.gradle.kts b/span-stacktrace/build.gradle.kts index 57f299754c..94cd5a36c8 100644 --- a/span-stacktrace/build.gradle.kts +++ b/span-stacktrace/build.gradle.kts @@ -15,7 +15,6 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") - compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") compileOnly("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") @@ -30,4 +29,7 @@ dependencies { testCompileOnly("com.google.auto.service:auto-service-annotations") testImplementation("io.opentelemetry:opentelemetry-exporter-logging") + + // allows to test inferred spans that should be filtered-out + testCompileOnly(project(":inferred-spans")) } diff --git a/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessor.java b/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessor.java index 441e074469..5a3e64c7c2 100644 --- a/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessor.java +++ b/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessor.java @@ -59,6 +59,17 @@ public void onEnding(ReadWriteSpan span) { if (!filterPredicate.test(span)) { return; } + + // Inferred spans are generated from sampling, so this span processor is not actually called + // when the span is active, and stack trace collection should be skipped. + // + // We only get such spans when the inferred spans processor executes before this span + // processor, which may be considered a configuration error. + boolean isInferred = span.getInstrumentationScopeInfo().getName().equals("inferred-spans"); + if (isInferred) { + return; + } + span.setAttribute(CodeAttributes.CODE_STACKTRACE, generateSpanEndStacktrace()); } diff --git a/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessorTest.java b/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessorTest.java index e5be320194..e89de83da5 100644 --- a/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessorTest.java +++ b/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceSpanProcessorTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; +import io.opentelemetry.contrib.inferredspans.InferredSpansProcessor; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder; @@ -49,10 +50,10 @@ void durationAndFiltering() { // over duration threshold checkSpanWithStackTrace("1ms", msToNs(2)); // under duration threshold - checkSpanWithoutStackTrace(YesPredicate.class, "2ms", msToNs(1)); + checkSpanWithoutStackTrace(YesPredicate.class, "2ms", msToNs(1), "test"); // filtering out span - checkSpanWithoutStackTrace(NoPredicate.class, "1ms", msToNs(20)); + checkSpanWithoutStackTrace(NoPredicate.class, "1ms", msToNs(20), "test"); } public static class YesPredicate implements Predicate { @@ -75,12 +76,12 @@ void defaultConfig() { long expectedDefault = msToNs(5); checkSpanWithStackTrace(null, expectedDefault); checkSpanWithStackTrace(null, expectedDefault + 1); - checkSpanWithoutStackTrace(YesPredicate.class, null, expectedDefault - 1); + checkSpanWithoutStackTrace(YesPredicate.class, null, expectedDefault - 1, "test"); } @Test void disabledConfig() { - checkSpanWithoutStackTrace(YesPredicate.class, "-1", 5); + checkSpanWithoutStackTrace(YesPredicate.class, "-1", 5, "test"); } @Test @@ -90,7 +91,13 @@ void spanWithExistingStackTrace() { "1ms", Duration.ofMillis(1).toNanos(), sb -> sb.setAttribute(CodeAttributes.CODE_STACKTRACE, "hello"), - stacktrace -> assertThat(stacktrace).isEqualTo("hello")); + stacktrace -> assertThat(stacktrace).isEqualTo("hello"), + "test"); + } + + @Test + void spanFromInferredSpansIgnored() { + checkSpanWithoutStackTrace(null, "1ms", msToNs(1), InferredSpansProcessor.TRACER_NAME); } private static void checkSpanWithStackTrace(String minDurationString, long spanDurationNanos) { @@ -102,19 +109,22 @@ private static void checkSpanWithStackTrace(String minDurationString, long spanD (stackTrace) -> assertThat(stackTrace) .describedAs("span stack trace should contain caller class name") - .contains(StackTraceSpanProcessorTest.class.getCanonicalName())); + .contains(StackTraceSpanProcessorTest.class.getCanonicalName()), + "test"); } private static void checkSpanWithoutStackTrace( Class> predicateClass, String minDurationString, - long spanDurationNanos) { + long spanDurationNanos, + String scopeName) { checkSpan( predicateClass, minDurationString, spanDurationNanos, Function.identity(), - (stackTrace) -> assertThat(stackTrace).describedAs("no stack trace expected").isNull()); + (stackTrace) -> assertThat(stackTrace).describedAs("no stack trace expected").isNull(), + scopeName); } private static void checkSpan( @@ -122,7 +132,8 @@ private static void checkSpan( String minDurationString, long spanDurationNanos, Function customizeSpanBuilder, - Consumer stackTraceCheck) { + Consumer stackTraceCheck, + String scopeName) { // must be re-created on every test as exporter is shut down on span processor close InMemorySpanExporter spansExporter = InMemorySpanExporter.create(); @@ -153,7 +164,7 @@ private static void checkSpan( try (OpenTelemetrySdk sdk = sdkBuilder.build().getOpenTelemetrySdk()) { - Tracer tracer = sdk.getTracer("test"); + Tracer tracer = sdk.getTracer(scopeName); Instant start = Instant.now(); Instant end = start.plusNanos(spanDurationNanos);