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: