diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java index 1233d54d3567d..3c2c3135a5b90 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java @@ -76,7 +76,8 @@ AdditionalBeanBuildItem ensureProducerIsRetained() { .addBeanClasses(OpenTelemetryProducer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.ResourceCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.SamplerCustomizer.class, - AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer.class) + AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer.class, + AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TextMapPropagatorCustomizers.class) .build(); } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTextMapPropagatorCustomizerTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTextMapPropagatorCustomizerTest.java new file mode 100644 index 0000000000000..a10924e35c4a9 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTextMapPropagatorCustomizerTest.java @@ -0,0 +1,89 @@ +package io.quarkus.opentelemetry.deployment; + +import static org.assertj.core.api.Assertions.*; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.TextMapPropagatorCustomizer; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class OpenTelemetryTextMapPropagatorCustomizerTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addClass(TestSpanExporter.class) + .addClass(TestSpanExporterProvider.class) + .addClass(TestTextMapPropagatorCustomizer.class) + .addAsResource("resource-config/application.properties", "application.properties") + .addAsResource( + "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")); + @Inject + TestSpanExporter spanExporter; + + @Test + void testSvcNameHasPriorityOverAppNameAndResourceAttr() { + RestAssured.when() + .get("/hello").then() + .statusCode(200) + .body(is("hello")); + RestAssured.when() + .get("/hello").then() + .statusCode(200) + .body(is("hello")); + RestAssured.when() + .get("/hello").then() + .statusCode(200) + .body(is("hello")); + + List spans = spanExporter.getFinishedSpanItems(3); + final SpanData server = spans.get(0); + assertEquals("GET /hello", server.getName()); + + assertThat(TestTextMapPropagatorCustomizer.PROPAGATORS).containsOnly(W3CBaggagePropagator.class.getName(), + W3CTraceContextPropagator.class.getName()); + } + + @Path("/hello") + public static class HelloResource { + @GET + public String hello() { + return "hello"; + } + } + + @Singleton + public static class TestTextMapPropagatorCustomizer implements TextMapPropagatorCustomizer { + + public static final Set PROPAGATORS = ConcurrentHashMap.newKeySet(); + + @Override + public TextMapPropagator customize(Context context) { + TextMapPropagator propagator = context.propagator(); + PROPAGATORS.add(propagator.getClass().getName()); + return propagator; + } + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/TextMapPropagatorCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/TextMapPropagatorCustomizer.java new file mode 100644 index 0000000000000..6911e69917338 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/TextMapPropagatorCustomizer.java @@ -0,0 +1,20 @@ +package io.quarkus.opentelemetry; + +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; + +/** + * /** + * Meant to be implemented by a CDI bean that provides arbitrary customization for the {@link TextMapPropagator} + * that are to be registered with OpenTelemetry + */ +public interface TextMapPropagatorCustomizer { + + TextMapPropagator customize(Context context); + + interface Context { + TextMapPropagator propagator(); + + ConfigProperties otelConfigProperties(); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java index 87c0f35ffff9f..cc9666624cd97 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java @@ -11,6 +11,7 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Singleton; +import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; @@ -19,6 +20,7 @@ import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.quarkus.arc.All; +import io.quarkus.opentelemetry.TextMapPropagatorCustomizer; import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig; import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; import io.quarkus.opentelemetry.runtime.exporter.otlp.RemoveableLateBoundBatchSpanProcessor; @@ -164,4 +166,38 @@ public SdkTracerProviderBuilder apply(SdkTracerProviderBuilder builder, }); } } + + @Singleton + final class TextMapPropagatorCustomizers implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer { + + private final List customizers; + + public TextMapPropagatorCustomizers(@All List customizers) { + this.customizers = customizers; + } + + @Override + public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) { + for (TextMapPropagatorCustomizer customizer : customizers) { + builder.addPropagatorCustomizer( + new BiFunction<>() { + @Override + public TextMapPropagator apply(TextMapPropagator textMapPropagator, + ConfigProperties configProperties) { + return customizer.customize(new TextMapPropagatorCustomizer.Context() { + @Override + public TextMapPropagator propagator() { + return textMapPropagator; + } + + @Override + public ConfigProperties otelConfigProperties() { + return configProperties; + } + }); + } + }); + } + } + } }