From f805076c5ccafdcd379e34e676f221c17f44704a Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Fri, 31 Jul 2026 11:06:13 +0800 Subject: [PATCH] feat(observability): add selectable tracing modes Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- server/skillhub-app/pom.xml | 13 ++ .../logging/CorrelationJsonProvider.java | 39 +++++- .../logging/SkillHubEcsEncoder.java | 7 +- .../SkillHubObservabilityProperties.java | 20 +++ .../tracing/SkillHubTracingConfiguration.java | 54 ++++++++ .../observability/tracing/TracingMode.java | 10 ++ ...cingModeAutoConfigurationImportFilter.java | 51 +++++++ .../observability/tracing/package-info.java | 4 + .../main/resources/META-INF/spring.factories | 2 + .../src/main/resources/application.yml | 12 ++ .../src/main/resources/logback-spring.xml | 4 + .../logging/SkillHubEcsEncoderTest.java | 26 ++++ .../SkillHubTracingConfigurationTest.java | 124 ++++++++++++++++++ ...ModeAutoConfigurationImportFilterTest.java | 65 +++++++++ 14 files changed, 426 insertions(+), 5 deletions(-) create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubObservabilityProperties.java create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfiguration.java create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingMode.java create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilter.java create mode 100644 server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/package-info.java create mode 100644 server/skillhub-app/src/main/resources/META-INF/spring.factories create mode 100644 server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfigurationTest.java create mode 100644 server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilterTest.java diff --git a/server/skillhub-app/pom.xml b/server/skillhub-app/pom.xml index 8914e00e..413e7eaf 100644 --- a/server/skillhub-app/pom.xml +++ b/server/skillhub-app/pom.xml @@ -26,6 +26,19 @@ io.micrometer micrometer-registry-prometheus + + io.micrometer + micrometer-tracing-bridge-otel + + + io.opentelemetry + opentelemetry-exporter-otlp + + + org.apache.skywalking + apm-toolkit-logback-1.x + 9.6.0 + net.logstash.logback logstash-logback-encoder diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/CorrelationJsonProvider.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/CorrelationJsonProvider.java index 814f33ce..82c87fd5 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/CorrelationJsonProvider.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/CorrelationJsonProvider.java @@ -3,8 +3,10 @@ package com.iflytek.skillhub.observability.logging; import ch.qos.logback.classic.spi.ILoggingEvent; import com.fasterxml.jackson.core.JsonGenerator; import net.logstash.logback.composite.AbstractJsonProvider; +import org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.LogbackMDCPatternConverter; import java.io.IOException; +import java.util.List; import java.util.Map; /** @@ -17,22 +19,51 @@ final class CorrelationJsonProvider extends AbstractJsonProvider private static final String SPAN_ID_KEY = "spanId"; private static final String EXTERNAL_TRACE_ID_KEY = "tid"; + private final LogbackMDCPatternConverter externalTraceIdConverter; + + CorrelationJsonProvider(boolean externalTraceIdEnabled) { + if (externalTraceIdEnabled) { + externalTraceIdConverter = new LogbackMDCPatternConverter(); + externalTraceIdConverter.setOptionList(List.of(EXTERNAL_TRACE_ID_KEY)); + externalTraceIdConverter.start(); + } else { + externalTraceIdConverter = null; + } + } + @Override public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException { Map mdc = event.getMDCPropertyMap(); - if (mdc == null || mdc.isEmpty()) { - return; - } + mdc = mdc == null ? Map.of() : mdc; writeIfPresent(generator, "request.id", mdc.get(REQUEST_ID_KEY)); writeIfPresent( generator, "trace.id", - firstPresent(mdc.get(TRACE_ID_KEY), mdc.get(EXTERNAL_TRACE_ID_KEY)) + firstPresent(mdc.get(TRACE_ID_KEY), externalTraceId(event, mdc)) ); writeIfPresent(generator, "span.id", mdc.get(SPAN_ID_KEY)); } + private String externalTraceId(ILoggingEvent event, Map mdc) { + String traceId = mdc.get(EXTERNAL_TRACE_ID_KEY); + if (!isPresent(traceId) && externalTraceIdConverter != null) { + traceId = externalTraceIdConverter.convert(event); + } + return normalizeExternalTraceId(traceId); + } + + private String normalizeExternalTraceId(String traceId) { + if (!isPresent(traceId)) { + return null; + } + String normalized = traceId.trim(); + if (normalized.regionMatches(true, 0, "TID:", 0, 4)) { + normalized = normalized.substring(4).trim(); + } + return "N/A".equalsIgnoreCase(normalized) ? null : normalized; + } + private String firstPresent(String preferred, String fallback) { return isPresent(preferred) ? preferred : fallback; } diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoder.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoder.java index 7761b49d..8dc728bb 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoder.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoder.java @@ -25,6 +25,7 @@ public class SkillHubEcsEncoder extends LoggingEventCompositeJsonEncoder { private String serviceName = "skillhub"; private String serviceVersion = "unknown"; private String serviceEnvironment = "local"; + private boolean externalTraceIdEnabled; @Override public void start() { @@ -48,6 +49,10 @@ public class SkillHubEcsEncoder extends LoggingEventCompositeJsonEncoder { this.serviceEnvironment = serviceEnvironment; } + public void setTracingMode(String tracingMode) { + this.externalTraceIdEnabled = "external-agent".equalsIgnoreCase(tracingMode); + } + private LoggingEventJsonProviders createProviders() { LoggingEventJsonProviders providers = new LoggingEventJsonProviders(); @@ -74,7 +79,7 @@ public class SkillHubEcsEncoder extends LoggingEventCompositeJsonEncoder { providers.addThreadName(thread); providers.addGlobalCustomFields(serviceFields()); - providers.addProvider(new CorrelationJsonProvider()); + providers.addProvider(new CorrelationJsonProvider(externalTraceIdEnabled)); ThrowableClassNameJsonProvider errorType = new ThrowableClassNameJsonProvider(); errorType.setFieldName("error.type"); diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubObservabilityProperties.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubObservabilityProperties.java new file mode 100644 index 00000000..c27a3bfc --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubObservabilityProperties.java @@ -0,0 +1,20 @@ +package com.iflytek.skillhub.observability.tracing; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Startup-time observability choices owned by SkillHub. + */ +@ConfigurationProperties(prefix = "skillhub.observability") +public class SkillHubObservabilityProperties { + + private TracingMode tracingMode = TracingMode.NONE; + + public TracingMode getTracingMode() { + return tracingMode; + } + + public void setTracingMode(TracingMode tracingMode) { + this.tracingMode = tracingMode; + } +} diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfiguration.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfiguration.java new file mode 100644 index 00000000..ea3872b2 --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfiguration.java @@ -0,0 +1,54 @@ +package com.iflytek.skillhub.observability.tracing; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +/** + * Validates tracing mode combinations that SkillHub can determine at startup. + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(SkillHubObservabilityProperties.class) +public class SkillHubTracingConfiguration { + + private static final Logger log = LoggerFactory.getLogger(SkillHubTracingConfiguration.class); + + @Bean + TracingModeGuard tracingModeGuard( + SkillHubObservabilityProperties properties, + Environment environment + ) { + TracingMode mode = properties.getTracingMode(); + String otlpEndpoint = environment.getProperty("management.otlp.tracing.endpoint"); + if (mode != TracingMode.OTEL_SDK && StringUtils.hasText(otlpEndpoint)) { + throw new IllegalStateException( + "management.otlp.tracing.endpoint requires " + + "skillhub.observability.tracing-mode=otel-sdk" + ); + } + if (mode == TracingMode.OTEL_SDK + && Boolean.FALSE.equals(environment.getProperty( + "management.tracing.enabled", + Boolean.class + ))) { + throw new IllegalStateException( + "management.tracing.enabled=false conflicts with " + + "skillhub.observability.tracing-mode=otel-sdk" + ); + } + if (mode == TracingMode.EXTERNAL_AGENT) { + log.warn( + "External tracing agent mode selected. SkillHub cannot verify the agent " + + "identity; deployment must provide exactly one tracing agent" + ); + } + return new TracingModeGuard(mode); + } + + record TracingModeGuard(TracingMode mode) { + } +} diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingMode.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingMode.java new file mode 100644 index 00000000..feaf1171 --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingMode.java @@ -0,0 +1,10 @@ +package com.iflytek.skillhub.observability.tracing; + +/** + * Selects the single tracing implementation that may be active in the application process. + */ +public enum TracingMode { + NONE, + OTEL_SDK, + EXTERNAL_AGENT +} diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilter.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilter.java new file mode 100644 index 00000000..55a33fda --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilter.java @@ -0,0 +1,51 @@ +package com.iflytek.skillhub.observability.tracing; + +import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter; +import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; + +import java.util.Set; + +/** + * Keeps the OpenTelemetry SDK outside the application context unless the deployment explicitly + * selects {@code otel-sdk}. The normal Spring Boot NOOP tracer remains available in the other + * modes. + */ +public final class TracingModeAutoConfigurationImportFilter + implements AutoConfigurationImportFilter, EnvironmentAware { + + static final String TRACING_MODE_PROPERTY = "skillhub.observability.tracing-mode"; + + private static final Set OTEL_AUTO_CONFIGURATIONS = Set.of( + "org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration", + "org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration", + "org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration" + ); + + private Environment environment; + + @Override + public boolean[] match( + String[] autoConfigurationClasses, + AutoConfigurationMetadata autoConfigurationMetadata + ) { + boolean otelSdkEnabled = environment != null + && "otel-sdk".equalsIgnoreCase( + environment.getProperty(TRACING_MODE_PROPERTY, "none") + ); + boolean[] matches = new boolean[autoConfigurationClasses.length]; + for (int index = 0; index < autoConfigurationClasses.length; index++) { + String autoConfigurationClass = autoConfigurationClasses[index]; + matches[index] = autoConfigurationClass != null + && (otelSdkEnabled + || !OTEL_AUTO_CONFIGURATIONS.contains(autoConfigurationClass)); + } + return matches; + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } +} diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/package-info.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/package-info.java new file mode 100644 index 00000000..a6043895 --- /dev/null +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/observability/tracing/package-info.java @@ -0,0 +1,4 @@ +/** + * Startup tracing mode selection and auto-configuration boundaries. + */ +package com.iflytek.skillhub.observability.tracing; diff --git a/server/skillhub-app/src/main/resources/META-INF/spring.factories b/server/skillhub-app/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..e8a70f68 --- /dev/null +++ b/server/skillhub-app/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ +com.iflytek.skillhub.observability.tracing.TracingModeAutoConfigurationImportFilter diff --git a/server/skillhub-app/src/main/resources/application.yml b/server/skillhub-app/src/main/resources/application.yml index 9983d5e8..10e9638e 100644 --- a/server/skillhub-app/src/main/resources/application.yml +++ b/server/skillhub-app/src/main/resources/application.yml @@ -95,6 +95,7 @@ spring: skillhub: observability: + tracing-mode: ${SKILLHUB_TRACING_MODE:none} log-format: ${SKILLHUB_LOG_FORMAT:text} log-async-queue-size: ${SKILLHUB_LOG_ASYNC_QUEUE_SIZE:1024} service-version: ${SKILLHUB_SERVICE_VERSION:unknown} @@ -214,6 +215,17 @@ skillhub: email: ${BOOTSTRAP_ADMIN_EMAIL:admin@skillhub.local} management: + tracing: + sampling: + probability: ${SKILLHUB_TRACING_SAMPLING_PROBABILITY:0.1} + baggage: + enabled: false + propagation: + type: W3C + otlp: + tracing: + timeout: ${SKILLHUB_OTLP_TIMEOUT:5s} + compression: ${SKILLHUB_OTLP_COMPRESSION:gzip} health: mail: enabled: ${MANAGEMENT_HEALTH_MAIL_ENABLED:false} diff --git a/server/skillhub-app/src/main/resources/logback-spring.xml b/server/skillhub-app/src/main/resources/logback-spring.xml index 30152e8e..c9372168 100644 --- a/server/skillhub-app/src/main/resources/logback-spring.xml +++ b/server/skillhub-app/src/main/resources/logback-spring.xml @@ -5,6 +5,9 @@ + @@ -30,6 +33,7 @@ ${serviceName} ${serviceVersion} ${serviceEnvironment} + ${tracingMode} diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoderTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoderTest.java index 9d0df479..e417d97e 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoderTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/logging/SkillHubEcsEncoderTest.java @@ -81,6 +81,32 @@ class SkillHubEcsEncoderTest { .hasSize(1); } + @Test + void shouldNormalizeExternalAgentTraceId() throws Exception { + encoder.stop(); + encoder.setTracingMode("external-agent"); + encoder.start(); + LoggingEvent event = event("external trace"); + event.setMDCPropertyMap(Map.of("tid", "TID: external-agent-trace")); + + JsonNode json = encode(event); + + assertThat(json.path("trace.id").asText()).isEqualTo("external-agent-trace"); + } + + @Test + void shouldNotWriteToolkitSentinelAsTraceId() throws Exception { + encoder.stop(); + encoder.setTracingMode("external-agent"); + encoder.start(); + LoggingEvent event = event("no external agent"); + event.setMDCPropertyMap(Map.of("tid", "TID: N/A")); + + JsonNode json = encode(event); + + assertThat(json.has("trace.id")).isFalse(); + } + @Test void shouldWriteStructuredExceptionFields() throws Exception { LoggingEvent event = event("failed"); diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfigurationTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfigurationTest.java new file mode 100644 index 00000000..1c5913ce --- /dev/null +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/SkillHubTracingConfigurationTest.java @@ -0,0 +1,124 @@ +package com.iflytek.skillhub.observability.tracing; + +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.otel.bridge.OtelTracer; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.assertj.core.api.Assertions.assertThat; + +class SkillHubTracingConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withUserConfiguration(TestApplication.class) + .withPropertyValues( + "spring.flyway.enabled=false", + "spring.jpa.hibernate.ddl-auto=none" + ); + + @Test + void noneModeShouldUseNoopTracerAndNoOtelSdk() { + contextRunner + .withPropertyValues("skillhub.observability.tracing-mode=none") + .run(context -> { + assertThat(context).hasNotFailed(); + assertThat(context.getBean(Tracer.class)).isSameAs(Tracer.NOOP); + assertThat(context).doesNotHaveBean(OpenTelemetry.class); + assertThat(context).doesNotHaveBean(OtlpHttpSpanExporter.class); + }); + } + + @Test + void externalAgentModeShouldUseNoopTracerAndNoOtelSdk() { + contextRunner + .withPropertyValues("skillhub.observability.tracing-mode=external-agent") + .run(context -> { + assertThat(context).hasNotFailed(); + assertThat(context.getBean(Tracer.class)).isSameAs(Tracer.NOOP); + assertThat(context).doesNotHaveBean(OpenTelemetry.class); + assertThat(context).doesNotHaveBean(OtlpHttpSpanExporter.class); + }); + } + + @Test + void otelSdkModeWithoutEndpointShouldCreateInProcessTracerOnly() { + contextRunner + .withPropertyValues( + "skillhub.observability.tracing-mode=otel-sdk", + "management.tracing.sampling.probability=1.0", + "management.tracing.baggage.enabled=false", + "management.tracing.propagation.type=W3C" + ) + .run(context -> { + assertThat(context).hasNotFailed(); + assertThat(context.getBean(Tracer.class)).isInstanceOf(OtelTracer.class); + assertThat(context).hasSingleBean(OpenTelemetry.class); + assertThat(context).doesNotHaveBean(OtlpHttpSpanExporter.class); + }); + } + + @Test + void otelSdkModeShouldCreateExporterOnlyWhenEndpointIsConfigured() { + contextRunner + .withPropertyValues( + "skillhub.observability.tracing-mode=otel-sdk", + "management.otlp.tracing.endpoint=http://127.0.0.1:4318/v1/traces" + ) + .run(context -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + }); + } + + @Test + void nonOtelModeShouldRejectConfiguredOtlpEndpoint() { + contextRunner + .withPropertyValues( + "skillhub.observability.tracing-mode=none", + "management.otlp.tracing.endpoint=http://127.0.0.1:4318/v1/traces" + ) + .run(context -> assertThat(context).hasFailed()); + } + + @Test + void otelSdkModeShouldRejectDisabledTracing() { + contextRunner + .withPropertyValues( + "skillhub.observability.tracing-mode=otel-sdk", + "management.tracing.enabled=false" + ) + .run(context -> assertThat(context).hasFailed()); + } + + @Test + void otelSdkScopeShouldPublishTraceCorrelationToMdc() { + contextRunner + .withPropertyValues( + "skillhub.observability.tracing-mode=otel-sdk", + "management.tracing.sampling.probability=1.0" + ) + .run(context -> { + Tracer tracer = context.getBean(Tracer.class); + io.micrometer.tracing.Span span = tracer.nextSpan().name("test-span").start(); + try (Tracer.SpanInScope ignored = tracer.withSpan(span)) { + assertThat(MDC.get("traceId")).hasSize(32); + assertThat(MDC.get("spanId")).hasSize(16); + } finally { + span.end(); + MDC.clear(); + } + }); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @Import(SkillHubTracingConfiguration.class) + static class TestApplication { + } +} diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilterTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilterTest.java new file mode 100644 index 00000000..6cfbb7d3 --- /dev/null +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/observability/tracing/TracingModeAutoConfigurationImportFilterTest.java @@ -0,0 +1,65 @@ +package com.iflytek.skillhub.observability.tracing; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class TracingModeAutoConfigurationImportFilterTest { + + private static final String CORE_OTEL_AUTO_CONFIGURATION = + "org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration"; + private static final String TRACING_OTEL_AUTO_CONFIGURATION = + "org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration"; + private static final String OTLP_AUTO_CONFIGURATION = + "org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpAutoConfiguration"; + private static final String NOOP_AUTO_CONFIGURATION = + "org.springframework.boot.actuate.autoconfigure.tracing.NoopTracerAutoConfiguration"; + + private final TracingModeAutoConfigurationImportFilter filter = + new TracingModeAutoConfigurationImportFilter(); + + @Test + void shouldExcludeApplicationOtelForDefaultNoneMode() { + filter.setEnvironment(new MockEnvironment()); + + assertThat(matches()).containsExactly(false, false, false, true, false); + } + + @Test + void shouldExcludeApplicationOtelForExternalAgentMode() { + filter.setEnvironment(new MockEnvironment() + .withProperty( + TracingModeAutoConfigurationImportFilter.TRACING_MODE_PROPERTY, + "external-agent" + )); + + assertThat(matches()).containsExactly(false, false, false, true, false); + } + + @Test + void shouldEnableApplicationOtelOnlyForOtelSdkMode() { + filter.setEnvironment(new MockEnvironment() + .withProperty( + TracingModeAutoConfigurationImportFilter.TRACING_MODE_PROPERTY, + "otel-sdk" + )); + + assertThat(matches()).containsExactly(true, true, true, true, false); + } + + private boolean[] matches() { + return filter.match( + new String[]{ + CORE_OTEL_AUTO_CONFIGURATION, + TRACING_OTEL_AUTO_CONFIGURATION, + OTLP_AUTO_CONFIGURATION, + NOOP_AUTO_CONFIGURATION, + null + }, + mock(AutoConfigurationMetadata.class) + ); + } +}