diff --git a/span-stacktrace/README.md b/span-stacktrace/README.md index 92eef3bbe..ecd31ac85 100644 --- a/span-stacktrace/README.md +++ b/span-stacktrace/README.md @@ -1,4 +1,3 @@ - # Span stacktrace capture This module provides a `SpanProcessor` that captures the [`code.stacktrace`](https://opentelemetry.io/docs/specs/semconv/attributes-registry/code/). @@ -25,6 +24,21 @@ SDK when included in the application runtime dependencies. - value is the class name of a class implementing `java.util.function.Predicate` - filter class must be publicly accessible and provide a no-arg constructor +### Usage with declarative configuration + +You can enable the stacktrace span processor using declarative YAML configuration with the OpenTelemetry SDK. For example: + +```yaml +file_format: 1.0-rc.1 +tracer_provider: + processors: + - experimental_stacktrace: + min_duration: 10 # minimal duration in ms, default is 5, MUST be an integer + filter: my.class.Name # optional, default is to include all spans +``` + +This configuration will register the StackTraceSpanProcessor for all spans. + ## Component owners - [Jack Shirazi](https://github.com/jackshirazi), Elastic diff --git a/span-stacktrace/build.gradle.kts b/span-stacktrace/build.gradle.kts index 4033b0177..57f299754 100644 --- a/span-stacktrace/build.gradle.kts +++ b/span-stacktrace/build.gradle.kts @@ -15,8 +15,13 @@ 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") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") + testImplementation("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge") compileOnly("io.opentelemetry.semconv:opentelemetry-semconv") testImplementation("io.opentelemetry.semconv:opentelemetry-semconv") diff --git a/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceAutoConfig.java b/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceAutoConfig.java index 3a427b7e3..4417c0f86 100644 --- a/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceAutoConfig.java +++ b/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceAutoConfig.java @@ -26,25 +26,26 @@ public class StackTraceAutoConfig implements AutoConfigurationCustomizerProvider private static final Logger log = Logger.getLogger(StackTraceAutoConfig.class.getName()); - private static final String CONFIG_MIN_DURATION = - "otel.java.experimental.span-stacktrace.min.duration"; + static final String PREFIX = "otel.java.experimental.span-stacktrace."; + static final String CONFIG_MIN_DURATION = PREFIX + "min.duration"; private static final Duration CONFIG_MIN_DURATION_DEFAULT = Duration.ofMillis(5); - - private static final String CONFIG_FILTER = "otel.java.experimental.span-stacktrace.filter"; + private static final String CONFIG_FILTER = PREFIX + "filter"; @Override public void customize(AutoConfigurationCustomizer config) { config.addTracerProviderCustomizer( (providerBuilder, properties) -> { - long minDuration = getMinDuration(properties); - if (minDuration >= 0) { - Predicate filter = getFilterPredicate(properties); - providerBuilder.addSpanProcessor(new StackTraceSpanProcessor(minDuration, filter)); + if (getMinDuration(properties) >= 0) { + providerBuilder.addSpanProcessor(create(properties)); } return providerBuilder; }); } + static StackTraceSpanProcessor create(ConfigProperties properties) { + return new StackTraceSpanProcessor(getMinDuration(properties), getFilterPredicate(properties)); + } + // package-private for testing static long getMinDuration(ConfigProperties properties) { long minDuration = diff --git a/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceComponentProvider.java b/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceComponentProvider.java new file mode 100644 index 000000000..338ebd960 --- /dev/null +++ b/span-stacktrace/src/main/java/io/opentelemetry/contrib/stacktrace/StackTraceComponentProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.stacktrace; + +import com.google.auto.service.AutoService; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; + +@SuppressWarnings("rawtypes") +@AutoService(ComponentProvider.class) +public class StackTraceComponentProvider implements ComponentProvider { + @Override + public String getName() { + return "experimental_stacktrace"; + } + + @Override + public SpanProcessor create(DeclarativeConfigProperties config) { + return StackTraceAutoConfig.create( + new DeclarativeConfigPropertiesBridgeBuilder() + .addMapping(StackTraceAutoConfig.CONFIG_MIN_DURATION, "min_duration") + .addMapping(StackTraceAutoConfig.PREFIX, "") + .build(config)); + } + + @Override + public Class getType() { + return SpanProcessor.class; + } +} 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 3565957b1..441e07446 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 @@ -94,4 +94,14 @@ private static String removeInternalFrames(String stackTrace) { } return stackTrace.substring(nextNewLine + 1); } + + @Override + public String toString() { + return "StackTraceSpanProcessor{" + + "minSpanDurationNanos=" + + minSpanDurationNanos + + ", filterPredicate=" + + filterPredicate + + '}'; + } } diff --git a/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceComponentProviderTest.java b/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceComponentProviderTest.java new file mode 100644 index 000000000..ee55d726c --- /dev/null +++ b/span-stacktrace/src/test/java/io/opentelemetry/contrib/stacktrace/StackTraceComponentProviderTest.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.stacktrace; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; + +class StackTraceComponentProviderTest { + @Test + void endToEnd() { + String yaml = + "file_format: 1.0-rc.1\n" + + "tracer_provider:\n" + + " processors:\n" + + " - experimental_stacktrace: \n" + + " min_duration: 100\n" + + " filter: io.opentelemetry.contrib.stacktrace.StackTraceSpanProcessorTest$YesPredicate\n"; + + OpenTelemetrySdk openTelemetrySdk = + DeclarativeConfiguration.parseAndCreate( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + + assertThat(openTelemetrySdk.getSdkTracerProvider().toString()) + .contains( + String.format( + Locale.ROOT, + "StackTraceSpanProcessor{minSpanDurationNanos=%d, " + + "filterPredicate=io.opentelemetry.contrib.stacktrace.StackTraceSpanProcessorTest$YesPredicate", + TimeUnit.MILLISECONDS.toNanos(100))); + } +}