Skip to content

Commit

Permalink
Spring use sdk autoconfig (#10453)
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitlinger committed Mar 13, 2024
1 parent b78316b commit b7e847c
Show file tree
Hide file tree
Showing 40 changed files with 467 additions and 1,594 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,31 @@

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpExporterProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpLogRecordExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.MapConverter;
import io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringConfigProperties;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtelResourceProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtlpExporterProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.properties.PropagationProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.properties.SpringConfigProperties;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.DistroVersionResourceProvider;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringResourceProvider;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.info.BuildProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.expression.spel.standard.SpelExpressionParser;

/**
* Create {@link io.opentelemetry.api.OpenTelemetry} bean if bean is missing.
Expand All @@ -60,7 +42,6 @@
*/
@Configuration
@EnableConfigurationProperties({
SamplerProperties.class,
OtlpExporterProperties.class,
OtelResourceProperties.class,
PropagationProperties.class
Expand All @@ -76,143 +57,54 @@ public static class OpenTelemetrySdkConfig {

@Bean
@ConfigurationPropertiesBinding
@Conditional(MapConverterCondition.class)
public MapConverter mapConverter() {
// needed for otlp exporter headers and OtelResourceProperties
return new MapConverter();
}

static final class MapConverterCondition extends AnyNestedCondition {
public MapConverterCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnBean(OtelResourceAutoConfiguration.class)
static class Resource {}

@ConditionalOnBean(OtlpLogRecordExporterAutoConfiguration.class)
static class Logger {}

@ConditionalOnBean(OtlpSpanExporterAutoConfiguration.class)
static class Span {}

@ConditionalOnBean(OtlpMetricExporterAutoConfiguration.class)
static class Metric {}
}

@Bean
@ConditionalOnMissingBean
ConfigProperties configProperties(
Environment env,
OtlpExporterProperties otlpExporterProperties,
OtelResourceProperties resourceProperties,
PropagationProperties propagationProperties) {
return new SpringConfigProperties(
env,
new SpelExpressionParser(),
otlpExporterProperties,
resourceProperties,
propagationProperties);
}

@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean
public SdkTracerProvider sdkTracerProvider(
SamplerProperties samplerProperties,
ObjectProvider<List<SpanExporter>> spanExportersProvider,
Resource otelResource) {
SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();

spanExportersProvider.getIfAvailable(Collections::emptyList).stream()
.map(spanExporter -> BatchSpanProcessor.builder(spanExporter).build())
.forEach(tracerProviderBuilder::addSpanProcessor);

return tracerProviderBuilder
.setResource(otelResource)
.setSampler(Sampler.traceIdRatioBased(samplerProperties.getProbability()))
.build();
}

@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean
public SdkLoggerProvider sdkLoggerProvider(
ObjectProvider<List<LogRecordExporter>> loggerExportersProvider, Resource otelResource) {

SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder();
loggerProviderBuilder.setResource(otelResource);

loggerExportersProvider
.getIfAvailable(Collections::emptyList)
.forEach(
loggerExporter ->
loggerProviderBuilder.addLogRecordProcessor(
BatchLogRecordProcessor.builder(loggerExporter).build()));

return loggerProviderBuilder.build();
public OpenTelemetrySdkComponentLoader openTelemetrySdkComponentLoader(
ApplicationContext applicationContext) {
return new OpenTelemetrySdkComponentLoader(applicationContext);
}

@Bean(destroyMethod = "") // SDK components are shutdown from the OpenTelemetry instance
@ConditionalOnMissingBean
public SdkMeterProvider sdkMeterProvider(
ConfigProperties configProperties,
ObjectProvider<List<MetricExporter>> metricExportersProvider,
Resource otelResource) {

SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();

metricExportersProvider.getIfAvailable(Collections::emptyList).stream()
.map(metricExporter -> createPeriodicMetricReader(configProperties, metricExporter))
.forEach(meterProviderBuilder::registerMetricReader);

return meterProviderBuilder.setResource(otelResource).build();
}

private static PeriodicMetricReader createPeriodicMetricReader(
ConfigProperties properties, MetricExporter metricExporter) {
PeriodicMetricReaderBuilder metricReaderBuilder =
PeriodicMetricReader.builder(metricExporter);
Duration interval = properties.getDuration("otel.metric.export.interval");
if (interval != null) {
metricReaderBuilder.setInterval(interval);
}
return metricReaderBuilder.build();
@Bean
public ResourceProvider otelSpringResourceProvider(Optional<BuildProperties> buildProperties) {
return new SpringResourceProvider(buildProperties);
}

@Bean
@ConditionalOnMissingBean
public Resource otelResource(
ConfigProperties config, ObjectProvider<List<ResourceProvider>> resourceProviders) {
Resource resource = Resource.getDefault();
for (ResourceProvider resourceProvider :
resourceProviders.getIfAvailable(Collections::emptyList)) {
resource = resource.merge(resourceProvider.createResource(config));
}
return resource;
public ResourceProvider otelDistroVersionResourceProvider() {
return new DistroVersionResourceProvider();
}

@Bean
// If you change the bean name, also change it in the OpenTelemetryJdbcDriverAutoConfiguration
// class
public OpenTelemetry openTelemetry(
ObjectProvider<ContextPropagators> propagatorsProvider,
SdkTracerProvider tracerProvider,
SdkMeterProvider meterProvider,
SdkLoggerProvider loggerProvider,
ObjectProvider<List<OpenTelemetryInjector>> openTelemetryConsumerProvider) {

ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop);

OpenTelemetrySdk openTelemetry =
OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.setLoggerProvider(loggerProvider)
.setPropagators(propagators)
.build();

List<OpenTelemetryInjector> openTelemetryInjectors =
openTelemetryConsumerProvider.getIfAvailable(() -> Collections.emptyList());
openTelemetryInjectors.forEach(consumer -> consumer.accept(openTelemetry));
Environment env,
OtlpExporterProperties otlpExporterProperties,
OtelResourceProperties resourceProperties,
PropagationProperties propagationProperties,
OpenTelemetrySdkComponentLoader componentLoader,
ObjectProvider<OpenTelemetryInjector> openTelemetryConsumerProvider) {

OpenTelemetry openTelemetry =
AutoConfigureUtil.setComponentLoader(
AutoConfigureUtil.setConfigPropertiesCustomizer(
AutoConfiguredOpenTelemetrySdk.builder(),
c ->
SpringConfigProperties.create(
env,
otlpExporterProperties,
resourceProperties,
propagationProperties,
c)),
componentLoader)
.build()
.getOpenTelemetrySdk();

openTelemetryConsumerProvider.forEach(consumer -> consumer.accept(openTelemetry));

return openTelemetry;
}
Expand All @@ -228,4 +120,29 @@ public OpenTelemetry openTelemetry() {
return OpenTelemetry.noop();
}
}

/**
* The {@link ComponentLoader} is used by the SDK autoconfiguration to load all components, e.g.
* <a
* href="https://github.com/open-telemetry/opentelemetry-java/blob/4519a7e90243e5b75b3a46a14c872de88b95a9a1/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java#L405-L408">here</a>
*/
public static class OpenTelemetrySdkComponentLoader implements ComponentLoader {
private final ApplicationContext applicationContext;

private final SpiHelper spiHelper =
SpiHelper.create(OpenTelemetrySdkComponentLoader.class.getClassLoader());

public OpenTelemetrySdkComponentLoader(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

@Override
public <T> Iterable<T> load(Class<T> spiClass) {
List<T> spi = spiHelper.load(spiClass);
List<T> beans =
applicationContext.getBeanProvider(spiClass).orderedStream().collect(Collectors.toList());
spi.addAll(beans);
return spi;
}
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit b7e847c

Please sign in to comment.