Skip to content

Commit 17c964d

Browse files
committed
Read declarative thread_details flag from ConfigProvider
- configureDeclarativeConfig now takes ConfigProvider and reads java.spring_starter.thread_details.enabled from the instrumentation config. The call site in OpenTelemetryAutoConfiguration passes SpringConfigProvider, which coerces Spring's flattened String values back to Boolean; the previous attempts via SdkConfigProvider/YamlDeclarativeConfigProperties silently dropped the flag because Spring's application.yaml pipeline stores every scalar as a String and YamlDeclarativeConfigProperties refuses to coerce. - configureProperties keeps reading otel.instrumentation.common.thread-details.enabled from Environment for the properties-based path; no legacy fallback in the declarative path. - Add declarative-config smoke test assertion: enable thread_details in the testDeclarativeConfig application.yaml and assert thread.id / thread.name appear on the server span. - Simplify segments[0] == "agent" guard in ConfigPropertiesBackedDeclarativeConfigProperties (drop dead segments.length check, work off translatedPath).
1 parent d63edad commit 17c964d

6 files changed

Lines changed: 48 additions & 34 deletions

File tree

declarative-config-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,15 @@ private String resolvePropertyKey(String name) {
226226
}
227227

228228
// java.agent.* maps to otel.javaagent.* (not otel.instrumentation.agent.*)
229-
if (segments.length > 0 && segments[0].equals("agent")) {
230-
if (translatedPath.length() == "agent".length()) {
231-
return "otel.javaagent";
232-
}
233-
return "otel.javaagent." + translatedPath.substring("agent.".length());
229+
String translated = translatedPath.toString();
230+
if (translated.equals("agent")) {
231+
return "otel.javaagent";
232+
}
233+
if (translated.startsWith("agent.")) {
234+
return "otel.javaagent." + translated.substring("agent.".length());
234235
}
235236

236-
return "otel.instrumentation." + translatedPath;
237+
return "otel.instrumentation." + translated;
237238
}
238239

239240
private String pathWithName(String name) {

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,14 @@ public OpenTelemetryConfigurationModel openTelemetryConfigurationModel(
168168
@Bean
169169
public OpenTelemetry openTelemetry(
170170
OpenTelemetryConfigurationModel model, ApplicationContext applicationContext) {
171-
ThreadDetailsInstrumenterCustomizerProvider.configureDeclarativeConfig(
172-
applicationContext.getEnvironment());
173171
OpenTelemetrySdkComponentLoader componentLoader =
174172
new OpenTelemetrySdkComponentLoader(applicationContext);
173+
SpringConfigProvider configProvider = SpringConfigProvider.create(model, componentLoader);
174+
ThreadDetailsInstrumenterCustomizerProvider.configureDeclarativeConfig(configProvider);
175175
OpenTelemetrySdk sdk = DeclarativeConfiguration.create(model, componentLoader).getSdk();
176176
Runtime.getRuntime().addShutdownHook(new Thread(sdk::close));
177177
logStart();
178-
return new SpringOpenTelemetrySdk(sdk, SpringConfigProvider.create(model, componentLoader));
178+
return new SpringOpenTelemetrySdk(sdk, configProvider);
179179
}
180180

181181
/**

instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsInstrumenterCustomizerProvider.java

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread;
77

8+
import io.opentelemetry.api.incubator.config.ConfigProvider;
89
import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizer;
910
import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizerProvider;
1011
import io.opentelemetry.instrumentation.api.incubator.thread.ThreadDetailsAttributesExtractor;
@@ -17,8 +18,6 @@
1718
public class ThreadDetailsInstrumenterCustomizerProvider implements InstrumenterCustomizerProvider {
1819
private static final String LEGACY_PROPERTY =
1920
"otel.instrumentation.common.thread-details.enabled";
20-
private static final String DECLARATIVE_PROPERTY =
21-
"otel.distribution.spring_starter.thread_details_enabled";
2221

2322
// Static because this class is loaded via ServiceLoader (SPI), not managed by Spring DI.
2423
// Set by OpenTelemetryAutoConfiguration#openTelemetry before instrumenters are built,
@@ -27,17 +26,19 @@ public class ThreadDetailsInstrumenterCustomizerProvider implements Instrumenter
2726
private static volatile boolean enabled;
2827

2928
/** Called from OpenTelemetryAutoConfiguration for declarative config. */
30-
public static void configureDeclarativeConfig(Environment environment) {
31-
// Declarative config uses the distro-scoped key, but we still accept the legacy property so
32-
// both Spring configuration paths resolve the same feature flag.
33-
enabled = readEnabled(environment, DECLARATIVE_PROPERTY, LEGACY_PROPERTY);
29+
public static void configureDeclarativeConfig(ConfigProvider configProvider) {
30+
enabled =
31+
configProvider
32+
.getInstrumentationConfig()
33+
.get("java")
34+
.get("spring_starter")
35+
.get("thread_details")
36+
.getBoolean("enabled", false);
3437
}
3538

3639
/** Called from OpenTelemetryAutoConfiguration for properties-based config. */
3740
public static void configureProperties(Environment environment) {
38-
// Properties-based config keeps the legacy toggle, but we fall back to the declarative key so
39-
// a single setting still works if it is shared across config styles.
40-
enabled = readEnabled(environment, LEGACY_PROPERTY, DECLARATIVE_PROPERTY);
41+
enabled = Boolean.TRUE.equals(environment.getProperty(LEGACY_PROPERTY, Boolean.class));
4142
}
4243

4344
@Override
@@ -46,14 +47,4 @@ public void customize(InstrumenterCustomizer customizer) {
4647
customizer.addAttributesExtractor(new ThreadDetailsAttributesExtractor<>());
4748
}
4849
}
49-
50-
private static boolean readEnabled(
51-
Environment environment, String primaryProperty, String fallbackProperty) {
52-
Boolean enabled = environment.getProperty(primaryProperty, Boolean.class);
53-
if (enabled != null) {
54-
return enabled;
55-
}
56-
enabled = environment.getProperty(fallbackProperty, Boolean.class);
57-
return Boolean.TRUE.equals(enabled);
58-
}
5950
}

instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/thread/ThreadDetailsInstrumenterCustomizerProviderTest.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55

66
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.thread;
77

8+
import static java.nio.charset.StandardCharsets.UTF_8;
89
import static org.mockito.ArgumentMatchers.any;
910
import static org.mockito.Mockito.mock;
1011
import static org.mockito.Mockito.verify;
1112
import static org.mockito.Mockito.verifyNoInteractions;
1213

1314
import io.opentelemetry.instrumentation.api.incubator.instrumenter.InstrumenterCustomizer;
15+
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
16+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
17+
import io.opentelemetry.sdk.internal.SdkConfigProvider;
18+
import java.io.ByteArrayInputStream;
1419
import org.junit.jupiter.api.AfterEach;
1520
import org.junit.jupiter.api.Test;
1621
import org.springframework.mock.env.MockEnvironment;
@@ -36,11 +41,20 @@ void configurePropertiesUsesLegacyProperty() {
3641
}
3742

3843
@Test
39-
void configureDeclarativeConfigUsesDistributionProperty() {
40-
MockEnvironment environment =
41-
new MockEnvironment()
42-
.withProperty("otel.distribution.spring_starter.thread_details_enabled", "true");
43-
ThreadDetailsInstrumenterCustomizerProvider.configureDeclarativeConfig(environment);
44+
@SuppressWarnings("StringConcatToTextBlock")
45+
void configureDeclarativeConfigReadsFromModel() {
46+
String yaml =
47+
"file_format: \"1.0\"\n"
48+
+ "instrumentation/development:\n"
49+
+ " java:\n"
50+
+ " spring_starter:\n"
51+
+ " thread_details:\n"
52+
+ " enabled: true\n";
53+
OpenTelemetryConfigurationModel model =
54+
DeclarativeConfiguration.parse(new ByteArrayInputStream(yaml.getBytes(UTF_8)));
55+
56+
ThreadDetailsInstrumenterCustomizerProvider.configureDeclarativeConfig(
57+
SdkConfigProvider.create(DeclarativeConfiguration.toConfigProperties(model)));
4458

4559
InstrumenterCustomizer customizer = mock(InstrumenterCustomizer.class);
4660
new ThreadDetailsInstrumenterCustomizerProvider().customize(customizer);

smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;
1212
import static io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME;
1313
import static io.opentelemetry.semconv.incubating.TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION;
14+
import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_ID;
15+
import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_NAME;
1416

1517
import io.opentelemetry.api.trace.SpanKind;
1618
import org.assertj.core.api.AbstractCharSequenceAssert;
@@ -72,7 +74,10 @@ void restTemplate() {
7274
satisfies(
7375
SERVICE_INSTANCE_ID,
7476
AbstractCharSequenceAssert::isNotBlank)))
75-
.hasAttribute(HTTP_ROUTE, "/ping"),
77+
.hasAttribute(HTTP_ROUTE, "/ping")
78+
.hasAttributesSatisfying(
79+
satisfies(THREAD_ID, val -> val.isNotZero()),
80+
satisfies(THREAD_NAME, val -> val.isNotBlank())),
7681
AbstractSpringStarterSmokeTest::withSpanAssert));
7782
}
7883
}

smoke-tests-otel-starter/spring-boot-2/src/testDeclarativeConfig/resources/application.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,6 @@ otel:
4646
emit_experimental_telemetry/development: true
4747
runtime_telemetry:
4848
emit_experimental_metrics/development: true
49+
spring_starter:
50+
thread_details:
51+
enabled: true

0 commit comments

Comments
 (0)