diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java index bd38bf973036..5eda67987364 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java @@ -22,6 +22,7 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelDisabled; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelEnabled; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.OtelMapConverter; +import io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread.ThreadDetailsCustomizerProvider; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelResourceProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtelSpringProperties; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.OtlpExporterProperties; @@ -196,6 +197,11 @@ public DeclarativeConfigurationCustomizerProvider distroConfigurationCustomizerP return new ResourceCustomizerProvider(); } + @Bean + public DeclarativeConfigurationCustomizerProvider threadDetailsCustomizerProvider() { + return new ThreadDetailsCustomizerProvider(); + } + @Bean public ComponentProvider distroComponentProvider() { return new DistroComponentProvider(); diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java index 91a0049a7dc8..906f2c9dfd13 100644 --- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsAutoConfiguration.java @@ -6,7 +6,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread; import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation; -import io.opentelemetry.instrumentation.thread.internal.AddThreadDetailsSpanProcessor; +import io.opentelemetry.instrumentation.thread.internal.ThreadDetailsSpanProcessor; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -24,7 +24,7 @@ public AutoConfigurationCustomizerProvider threadDetailOtelCustomizer() { return p -> p.addTracerProviderCustomizer( (builder, config) -> { - builder.addSpanProcessor(new AddThreadDetailsSpanProcessor()); + builder.addSpanProcessor(new ThreadDetailsSpanProcessor()); return builder; }); } diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsCustomizerProvider.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsCustomizerProvider.java new file mode 100644 index 000000000000..9ac204173a5d --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsCustomizerProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread; + +import io.opentelemetry.instrumentation.thread.internal.AbstractThreadDetailsCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.DistributionPropertyModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; + +/** + * Adds thread details span attributes when enabled via the {@code distribution.spring_starter} + * node. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ThreadDetailsCustomizerProvider extends AbstractThreadDetailsCustomizerProvider { + + @Override + protected boolean isEnabled(OpenTelemetryConfigurationModel model) { + DistributionModel distribution = model.getDistribution(); + if (distribution == null) { + return false; + } + DistributionPropertyModel springStarter = + distribution.getAdditionalProperties().get("spring_starter"); + if (springStarter == null) { + return false; + } + Object enabled = springStarter.getAdditionalProperties().get("thread_details_enabled"); + if (enabled instanceof Boolean) { + return (Boolean) enabled; + } + // a String when set via environment variable substitution (thread_details_enabled: ${OTEL_...}) + return enabled instanceof String && Boolean.parseBoolean((String) enabled); + } +} diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsCustomizerProviderTest.java b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsCustomizerProviderTest.java new file mode 100644 index 000000000000..7cd3d47e329d --- /dev/null +++ b/instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsCustomizerProviderTest.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class ThreadDetailsCustomizerProviderTest { + + @ParameterizedTest + @CsvSource( + nullValues = "NULL", + value = { + "true, true", + "false, false", + "NULL, false", // distribution node not set + ", false", // thread_details_enabled key present with no value + "invalid, false", + }) + @SuppressWarnings("StringConcatToTextBlock") // latest dep allows text blocks + void isEnabled(String propertyValue, boolean expected) { + String enabled = + propertyValue == null + ? "" + : "distribution:\n" + + " spring_starter:\n" + + " thread_details_enabled: " + + propertyValue + + "\n"; + + String yaml = "file_format: \"1.0\"\n" + enabled; + + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse(new ByteArrayInputStream(yaml.getBytes(UTF_8))); + + assertThat(new ThreadDetailsCustomizerProvider().isEnabled(model)).isEqualTo(expected); + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfig.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfig.java index 4feac8bef6ca..eb2ae5d07591 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfig.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfig.java @@ -39,6 +39,8 @@ public class AgentDistributionConfig { private final List excludeClassLoaders; + private final boolean threadDetailsEnabled; + private final InstrumentationConfig instrumentation; public static AgentDistributionConfig get() { @@ -86,6 +88,7 @@ public static void set(AgentDistributionConfig distributionConfig) { Boolean forceSynchronousAgentListeners, @Nullable @JsonProperty("exclude_classes") List excludeClasses, @Nullable @JsonProperty("exclude_class_loaders") List excludeClassLoaders, + @Nullable @JsonProperty("thread_details_enabled") Boolean threadDetailsEnabled, @Nullable @JsonProperty("instrumentation") InstrumentationConfig instrumentation) { this.indyEnabled = indyEnabled != null ? indyEnabled : false; this.forceSynchronousAgentListeners = @@ -94,12 +97,13 @@ public static void set(AgentDistributionConfig distributionConfig) { excludeClasses != null ? new ArrayList<>(excludeClasses) : new ArrayList<>(); this.excludeClassLoaders = excludeClassLoaders != null ? new ArrayList<>(excludeClassLoaders) : new ArrayList<>(); + this.threadDetailsEnabled = threadDetailsEnabled != null ? threadDetailsEnabled : false; this.instrumentation = instrumentation != null ? instrumentation : new InstrumentationConfig(); } // Default constructor for testing AgentDistributionConfig() { - this(null, null, null, null, null); + this(null, null, null, null, null, null); } /** @@ -162,6 +166,10 @@ public boolean isIndyEnabled() { return indyEnabled; } + public boolean isThreadDetailsEnabled() { + return threadDetailsEnabled; + } + public boolean isForceSynchronousAgentListeners() { return forceSynchronousAgentListeners; } @@ -227,6 +235,9 @@ private static final class ConfigPropertiesAgentDistributionConfig "otel.javaagent.experimental.force-synchronous-agent-listeners", false), configProperties.getList("otel.javaagent.exclude-classes"), configProperties.getList("otel.javaagent.exclude-class-loaders"), + configProperties.getBoolean( + "otel.javaagent.add-thread-details", + !configProperties.getBoolean("otel.instrumentation.common.v3-preview", false)), null); this.configProperties = configProperties; } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java index ca33644b0032..2a4cd344bc4b 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java @@ -10,7 +10,8 @@ import com.google.auto.service.AutoService; import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.instrumentation.thread.internal.AddThreadDetailsSpanProcessor; +import io.opentelemetry.instrumentation.thread.internal.ThreadDetailsSpanProcessor; +import io.opentelemetry.javaagent.extension.instrumentation.internal.AgentDistributionConfig; import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; @@ -20,8 +21,6 @@ @AutoService(AutoConfigurationCustomizerProvider.class) public class AgentTracerProviderConfigurer implements AutoConfigurationCustomizerProvider { - private static final String ADD_THREAD_DETAILS = "otel.javaagent.add-thread-details"; - @Override public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { autoConfigurationCustomizer.addTracerProviderCustomizer( @@ -32,25 +31,19 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { private static SdkTracerProviderBuilder configure( SdkTracerProviderBuilder sdkTracerProviderBuilder, ConfigProperties config) { - // Register additional thread details logging span processor - boolean v3Preview = config.getBoolean("otel.instrumentation.common.v3-preview", false); - if (config.getBoolean(ADD_THREAD_DETAILS, !v3Preview)) { - sdkTracerProviderBuilder.addSpanProcessor(new AddThreadDetailsSpanProcessor()); + if (AgentDistributionConfig.fromConfigProperties(config).isThreadDetailsEnabled()) { + sdkTracerProviderBuilder.addSpanProcessor(new ThreadDetailsSpanProcessor()); } - maybeEnableLoggingExporter(sdkTracerProviderBuilder, config); - - return sdkTracerProviderBuilder; - } - - private static void maybeEnableLoggingExporter( - SdkTracerProviderBuilder builder, ConfigProperties config) { if (EarlyInitAgentConfig.get().isDebug()) { // don't install another instance if the user has already explicitly requested it. if (loggingExporterIsNotAlreadyConfigured(config)) { - builder.addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())); + sdkTracerProviderBuilder.addSpanProcessor( + SimpleSpanProcessor.create(LoggingSpanExporter.create())); } } + + return sdkTracerProviderBuilder; } private static boolean loggingExporterIsNotAlreadyConfigured(ConfigProperties config) { diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ThreadDetailsCustomizerProvider.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ThreadDetailsCustomizerProvider.java new file mode 100644 index 000000000000..030134b35d09 --- /dev/null +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/config/ThreadDetailsCustomizerProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.tooling.config; + +import com.google.auto.service.AutoService; +import io.opentelemetry.instrumentation.thread.internal.AbstractThreadDetailsCustomizerProvider; +import io.opentelemetry.javaagent.extension.instrumentation.internal.AgentDistributionConfig; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; + +/** Adds thread details span attributes when enabled via the {@code distribution.javaagent} node. */ +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public final class ThreadDetailsCustomizerProvider extends AbstractThreadDetailsCustomizerProvider { + + @Override + public int order() { + // run after JavaagentDistributionAccessCustomizerProvider (default order) has populated + // AgentDistributionConfig from the distribution.javaagent node + return 1; + } + + @Override + protected boolean isEnabled(OpenTelemetryConfigurationModel model) { + return AgentDistributionConfig.get().isThreadDetailsEnabled(); + } +} diff --git a/javaagent-tooling/src/testDistributionConfig/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfigTest.java b/javaagent-tooling/src/testDistributionConfig/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfigTest.java index 4c81c1dfdbca..be67f3402b63 100644 --- a/javaagent-tooling/src/testDistributionConfig/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfigTest.java +++ b/javaagent-tooling/src/testDistributionConfig/java/io/opentelemetry/javaagent/extension/instrumentation/internal/AgentDistributionConfigTest.java @@ -32,6 +32,11 @@ void testForceSynchronousAgentListeners() { assertThat(AgentDistributionConfig.get().isForceSynchronousAgentListeners()).isFalse(); } + @Test + void testThreadDetailsEnabled() { + assertThat(AgentDistributionConfig.get().isThreadDetailsEnabled()).isTrue(); + } + @Test void testExcludeClasses() { assertThat(AgentDistributionConfig.get().getExcludeClasses()) diff --git a/javaagent-tooling/src/testDistributionConfig/resources/distribution-config.yaml b/javaagent-tooling/src/testDistributionConfig/resources/distribution-config.yaml index 92c105894aba..417cdc037af1 100644 --- a/javaagent-tooling/src/testDistributionConfig/resources/distribution-config.yaml +++ b/javaagent-tooling/src/testDistributionConfig/resources/distribution-config.yaml @@ -9,6 +9,7 @@ distribution: javaagent: indy/development: true force_synchronous_agent_listeners/development: false + thread_details_enabled: true exclude_classes: - com.example.excluded.Class1 - com.example.excluded.Class2 diff --git a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AbstractThreadDetailsCustomizerProvider.java b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AbstractThreadDetailsCustomizerProvider.java new file mode 100644 index 000000000000..1eb743058e43 --- /dev/null +++ b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AbstractThreadDetailsCustomizerProvider.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thread.internal; + +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorPropertyModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.TracerProviderModel; +import java.util.ArrayList; +import java.util.List; + +/** + * Adds the {@link ThreadDetailsSpanProcessor} (via the {@link ThreadDetailsComponentProvider}) to + * the declarative configuration model when {@link #isEnabled(OpenTelemetryConfigurationModel)} + * returns {@code true}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public abstract class AbstractThreadDetailsCustomizerProvider + implements DeclarativeConfigurationCustomizerProvider { + + protected abstract boolean isEnabled(OpenTelemetryConfigurationModel model); + + @Override + public void customize(DeclarativeConfigurationCustomizer customizer) { + customizer.addModelCustomizer( + model -> { + maybeAddThreadDetailsProcessor(model); + return model; + }); + } + + private void maybeAddThreadDetailsProcessor(OpenTelemetryConfigurationModel model) { + if (!isEnabled(model)) { + return; + } + TracerProviderModel tracerProvider = model.getTracerProvider(); + if (tracerProvider == null) { + tracerProvider = new TracerProviderModel(); + model.withTracerProvider(tracerProvider); + } + List processors = tracerProvider.getProcessors(); + if (processors == null) { + processors = new ArrayList<>(); + tracerProvider.withProcessors(processors); + } + processors.add( + new SpanProcessorModel() + .withAdditionalProperty( + ThreadDetailsComponentProvider.NAME, new SpanProcessorPropertyModel())); + } +} diff --git a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsComponentProvider.java b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsComponentProvider.java new file mode 100644 index 000000000000..20c1d7226141 --- /dev/null +++ b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsComponentProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thread.internal; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; + +/** + * Declarative configuration component provider that exposes {@link ThreadDetailsSpanProcessor} + * under the name {@value #NAME}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ThreadDetailsComponentProvider implements ComponentProvider { + + public static final String NAME = "thread_details"; + + @Override + public Class getType() { + return SpanProcessor.class; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public SpanProcessor create(DeclarativeConfigProperties config) { + return new ThreadDetailsSpanProcessor(); + } +} diff --git a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessor.java b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsSpanProcessor.java similarity index 95% rename from sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessor.java rename to sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsSpanProcessor.java index b0036384eebc..5a17a74cd0e6 100644 --- a/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessor.java +++ b/sdk-autoconfigure-support/src/main/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsSpanProcessor.java @@ -19,7 +19,7 @@ * This class is internal and is hence not for public use. Its APIs are unstable and can change at * any time. */ -public class AddThreadDetailsSpanProcessor implements SpanProcessor { +public class ThreadDetailsSpanProcessor implements SpanProcessor { // attributes are not stable yet public static final AttributeKey THREAD_ID = longKey("thread.id"); diff --git a/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider b/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider new file mode 100644 index 000000000000..ff5788b43774 --- /dev/null +++ b/sdk-autoconfigure-support/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider @@ -0,0 +1 @@ +io.opentelemetry.instrumentation.thread.internal.ThreadDetailsComponentProvider diff --git a/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AbstractThreadDetailsCustomizerProviderTest.java b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AbstractThreadDetailsCustomizerProviderTest.java new file mode 100644 index 000000000000..8823b5b2e94e --- /dev/null +++ b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AbstractThreadDetailsCustomizerProviderTest.java @@ -0,0 +1,101 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thread.internal; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.logs.export.LogRecordExporter; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class AbstractThreadDetailsCustomizerProviderTest { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void addsThreadDetailsProcessorWhenEnabled(boolean enabled) { + OpenTelemetryConfigurationModel model = + applyCustomizer( + DeclarativeConfiguration.parse( + new ByteArrayInputStream("file_format: \"1.0\"\n".getBytes(UTF_8))), + new TestCustomizerProvider(enabled)); + + assertThat(threadDetailsProcessorPresent(model)).isEqualTo(enabled); + } + + private static boolean threadDetailsProcessorPresent(OpenTelemetryConfigurationModel model) { + if (model.getTracerProvider() == null || model.getTracerProvider().getProcessors() == null) { + return false; + } + return model.getTracerProvider().getProcessors().stream() + .anyMatch(processor -> processor.getAdditionalProperties().containsKey("thread_details")); + } + + private static OpenTelemetryConfigurationModel applyCustomizer( + OpenTelemetryConfigurationModel model, AbstractThreadDetailsCustomizerProvider provider) { + List> customizers = + new ArrayList<>(); + provider.customize(new ModelCustomizerCollector(customizers)); + for (Function customizer : + customizers) { + model = customizer.apply(model); + } + return model; + } + + private static class TestCustomizerProvider extends AbstractThreadDetailsCustomizerProvider { + private final boolean enabled; + + TestCustomizerProvider(boolean enabled) { + this.enabled = enabled; + } + + @Override + protected boolean isEnabled(OpenTelemetryConfigurationModel model) { + return enabled; + } + } + + private static class ModelCustomizerCollector implements DeclarativeConfigurationCustomizer { + private final List> + customizers; + + ModelCustomizerCollector( + List> + customizers) { + this.customizers = customizers; + } + + @Override + public void addModelCustomizer( + Function customizer) { + customizers.add(customizer); + } + + @Override + public void addSpanExporterCustomizer( + Class exporterType, BiFunction customizer) {} + + @Override + public void addMetricExporterCustomizer( + Class exporterType, BiFunction customizer) {} + + @Override + public void addLogRecordExporterCustomizer( + Class exporterType, BiFunction customizer) {} + } +} diff --git a/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessorTest.java b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsSpanProcessorTest.java similarity index 90% rename from sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessorTest.java rename to sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsSpanProcessorTest.java index 74d126aa8878..510fe0739e72 100644 --- a/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/AddThreadDetailsSpanProcessorTest.java +++ b/sdk-autoconfigure-support/src/test/java/io/opentelemetry/instrumentation/thread/internal/ThreadDetailsSpanProcessorTest.java @@ -17,11 +17,11 @@ import io.opentelemetry.sdk.trace.SpanProcessor; import org.junit.jupiter.api.Test; -class AddThreadDetailsSpanProcessorTest { +class ThreadDetailsSpanProcessorTest { private final ReadWriteSpan span = mock(ReadWriteSpan.class); - private final SpanProcessor spanProcessor = new AddThreadDetailsSpanProcessor(); + private final SpanProcessor spanProcessor = new ThreadDetailsSpanProcessor(); @Test void onStart() { diff --git a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java index 421615cf7e7e..aea024ed0206 100644 --- a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java +++ b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java @@ -11,6 +11,8 @@ import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; import static io.opentelemetry.semconv.TelemetryAttributes.TELEMETRY_DISTRO_NAME; import static io.opentelemetry.semconv.TelemetryAttributes.TELEMETRY_DISTRO_VERSION; +import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_ID; +import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_NAME; import io.opentelemetry.api.trace.SpanKind; import org.assertj.core.api.AbstractCharSequenceAssert; @@ -72,7 +74,9 @@ void restTemplate() { satisfies( SERVICE_INSTANCE_ID, AbstractCharSequenceAssert::isNotBlank))) - .hasAttribute(HTTP_ROUTE, "/ping"), + .hasAttribute(HTTP_ROUTE, "/ping") + .hasAttribute(satisfies(THREAD_ID, val -> val.isNotZero())) + .hasAttribute(satisfies(THREAD_NAME, val -> val.isNotBlank())), AbstractSpringStarterSmokeTest::withSpanAssert)); } } diff --git a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml index 7eee6adcb975..db65bdb2e615 100644 --- a/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml +++ b/smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml @@ -38,6 +38,10 @@ otel: - tracecontext: - baggage: + distribution: + spring_starter: + thread_details_enabled: true + instrumentation/development: java: common: