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 3c2c3135a5b90..0b80f7a871490 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 @@ -9,13 +9,20 @@ import java.util.Optional; import java.util.Set; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Singleton; + import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.config.ConfigValue; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; +import org.jboss.jandex.ParameterizedType; +import org.jboss.jandex.Type; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.exporter.otlp.internal.OtlpSpanExporterProvider; import io.opentelemetry.instrumentation.annotations.SpanAttribute; @@ -31,6 +38,7 @@ import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; import io.quarkus.arc.deployment.InterceptorBindingRegistrarBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem; import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.InterceptorBindingRegistrar; @@ -49,10 +57,11 @@ import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.util.ServiceUtil; import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.opentelemetry.OpenTelemetryDestroyer; import io.quarkus.opentelemetry.runtime.AutoConfiguredOpenTelemetrySdkBuilderCustomizer; -import io.quarkus.opentelemetry.runtime.OpenTelemetryProducer; import io.quarkus.opentelemetry.runtime.OpenTelemetryRecorder; import io.quarkus.opentelemetry.runtime.QuarkusContextStorage; +import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; import io.quarkus.opentelemetry.runtime.tracing.cdi.WithSpanInterceptor; import io.quarkus.opentelemetry.runtime.tracing.intrumentation.InstrumentationRecorder; import io.quarkus.runtime.LaunchMode; @@ -73,7 +82,7 @@ public class OpenTelemetryProcessor { AdditionalBeanBuildItem ensureProducerIsRetained() { return AdditionalBeanBuildItem.builder() .setUnremovable() - .addBeanClasses(OpenTelemetryProducer.class, + .addBeanClasses( AutoConfiguredOpenTelemetrySdkBuilderCustomizer.ResourceCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.SamplerCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer.class, @@ -81,6 +90,26 @@ AdditionalBeanBuildItem ensureProducerIsRetained() { .build(); } + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + SyntheticBeanBuildItem openTelemetryBean(OpenTelemetryRecorder recorder, OTelRuntimeConfig oTelRuntimeConfig) { + return SyntheticBeanBuildItem.configure(OpenTelemetry.class) + .defaultBean() + .setRuntimeInit() + .unremovable() + .scope(Singleton.class) + .addInjectionPoint( + ParameterizedType.create( + DotName.createSimple(Instance.class), + new Type[] { ClassType.create( + DotName.createSimple( + AutoConfiguredOpenTelemetrySdkBuilderCustomizer.class.getName())) }, + null)) + .createWith(recorder.opentelemetryBean(oTelRuntimeConfig)) + .destroyer(OpenTelemetryDestroyer.class) + .done(); + } + @BuildStep void registerNativeImageResources(BuildProducer services, BuildProducer removedResources, diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java new file mode 100644 index 0000000000000..67bacb2fc9a0b --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java @@ -0,0 +1,21 @@ +package io.quarkus.opentelemetry; + +import java.util.Map; + +import jakarta.enterprise.context.spi.CreationalContext; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.quarkus.arc.BeanDestroyer; + +public class OpenTelemetryDestroyer implements BeanDestroyer { + @Override + public void destroy(OpenTelemetry openTelemetry, CreationalContext creationalContext, + Map params) { + if (openTelemetry instanceof OpenTelemetrySdk) { + var openTelemetrySdk = ((OpenTelemetrySdk) openTelemetry); + openTelemetrySdk.getSdkTracerProvider().forceFlush(); + openTelemetrySdk.getSdkTracerProvider().shutdown(); + } + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryProducer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryProducer.java deleted file mode 100644 index 254df7db04955..0000000000000 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryProducer.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.quarkus.opentelemetry.runtime; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import jakarta.enterprise.inject.Disposes; -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Singleton; - -import org.eclipse.microprofile.config.ConfigProvider; - -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; -import io.quarkus.arc.All; -import io.quarkus.arc.DefaultBean; -import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; -import io.smallrye.config.ConfigValue; -import io.smallrye.config.NameIterator; -import io.smallrye.config.SmallRyeConfig; - -@Singleton -public class OpenTelemetryProducer { - - public void disposeOfOpenTelemetry(@Disposes OpenTelemetry openTelemetry) { - if (openTelemetry instanceof OpenTelemetrySdk) { - var openTelemetrySdk = ((OpenTelemetrySdk) openTelemetry); - openTelemetrySdk.getSdkTracerProvider().forceFlush(); - openTelemetrySdk.getSdkTracerProvider().shutdown(); - } - } - - @Produces - @Singleton - @DefaultBean - public OpenTelemetry getOpenTelemetry(OTelRuntimeConfig oTelRuntimeConfig, - @All List builderCustomizers) { - final Map oTelConfigs = getOtelConfigs(); - - if (oTelRuntimeConfig.sdkDisabled()) { - return AutoConfiguredOpenTelemetrySdk.builder() - .setResultAsGlobal(true) - .registerShutdownHook(false) - .addPropertiesSupplier(() -> oTelConfigs) - .build() - .getOpenTelemetrySdk(); - } - - var builder = AutoConfiguredOpenTelemetrySdk.builder() - .setResultAsGlobal(true) - .registerShutdownHook(false) - .addPropertiesSupplier(() -> oTelConfigs) - .setServiceClassLoader(Thread.currentThread().getContextClassLoader()); - for (var customizer : builderCustomizers) { - customizer.customize(builder); - } - - return builder.build().getOpenTelemetrySdk(); - } - - private Map getOtelConfigs() { - Map oTelConfigs = new HashMap<>(); - SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); - - // instruct OTel that we are using the AutoConfiguredOpenTelemetrySdk - oTelConfigs.put("otel.java.global-autoconfigure.enabled", "true"); - - // load new properties - for (String propertyName : config.getPropertyNames()) { - if (propertyName.startsWith("quarkus.otel.")) { - ConfigValue configValue = config.getConfigValue(propertyName); - if (configValue.getValue() != null) { - NameIterator name = new NameIterator(propertyName); - name.next(); - oTelConfigs.put(name.getName().substring(name.getPosition() + 1), configValue.getValue()); - } - } - } - return oTelConfigs; - } -} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java index 47939edcbc01d..dab2f72463113 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java @@ -1,12 +1,27 @@ package io.quarkus.opentelemetry.runtime; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; import java.util.function.Supplier; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.util.TypeLiteral; + +import org.eclipse.microprofile.config.ConfigProvider; + import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.events.GlobalEventEmitterProvider; import io.opentelemetry.api.logs.GlobalLoggerProvider; import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; import io.quarkus.runtime.annotations.Recorder; +import io.smallrye.config.ConfigValue; +import io.smallrye.config.NameIterator; +import io.smallrye.config.SmallRyeConfig; import io.vertx.core.Vertx; @Recorder @@ -31,4 +46,60 @@ public void storeVertxOnContextStorage(Supplier vertx) { QuarkusContextStorage.vertx = vertx.get(); } + /* RUNTIME INIT */ + public Function, OpenTelemetry> opentelemetryBean( + OTelRuntimeConfig oTelRuntimeConfig) { + return new Function<>() { + @Override + public OpenTelemetry apply(SyntheticCreationalContext context) { + Instance builderCustomizers = context + .getInjectedReference(new TypeLiteral<>() { + }); + + final Map oTelConfigs = getOtelConfigs(); + + if (oTelRuntimeConfig.sdkDisabled()) { + return AutoConfiguredOpenTelemetrySdk.builder() + .setResultAsGlobal(true) + .registerShutdownHook(false) + .addPropertiesSupplier(() -> oTelConfigs) + .build() + .getOpenTelemetrySdk(); + } + + var builder = AutoConfiguredOpenTelemetrySdk.builder() + .setResultAsGlobal(true) + .registerShutdownHook(false) + .addPropertiesSupplier(() -> oTelConfigs) + .setServiceClassLoader(Thread.currentThread().getContextClassLoader()); + for (var customizer : builderCustomizers) { + customizer.customize(builder); + } + + return builder.build().getOpenTelemetrySdk(); + } + + private Map getOtelConfigs() { + Map oTelConfigs = new HashMap<>(); + SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); + + // instruct OTel that we are using the AutoConfiguredOpenTelemetrySdk + oTelConfigs.put("otel.java.global-autoconfigure.enabled", "true"); + + // load new properties + for (String propertyName : config.getPropertyNames()) { + if (propertyName.startsWith("quarkus.otel.")) { + ConfigValue configValue = config.getConfigValue(propertyName); + if (configValue.getValue() != null) { + NameIterator name = new NameIterator(propertyName); + name.next(); + oTelConfigs.put(name.getName().substring(name.getPosition() + 1), configValue.getValue()); + } + } + } + return oTelConfigs; + } + }; + } + } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java index 66a3cbff07978..da44a0084b5b4 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java @@ -1,12 +1,10 @@ package io.quarkus.opentelemetry.runtime.exporter.otlp; -import io.quarkus.opentelemetry.runtime.OpenTelemetryProducer; +import io.quarkus.opentelemetry.runtime.AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer; /** - * The only point in having this class is to allow {@link OpenTelemetryProducer} + * The only point in having this class is to allow {@link TracerProviderCustomizer} * to easily ignore the configured {@link LateBoundBatchSpanProcessor}. - *

- * In the future when {@link OpenTelemetryProducer} is replaced by a synthetic bean, this class will no longer be necessary */ public final class RemoveableLateBoundBatchSpanProcessor extends LateBoundBatchSpanProcessor {