Hi!
I would like to propose an improvement in the way the OpenTelemetry metrics and tracing are auto-configured.
This is related to the issue: #30156 and my comment at the end.
As a starting point, the tracing OpenTelemetryAutoConfiguration sets up an instance of the OpenTelemetry and fetches a list of tracing providers. It is a great way to specify a non-supported exporter (e.g. OtlpGrpcSpanExporter instead of brave/zipkin/wavefront).
But the OtlpMetricsExportAutoConfiguration does not use the OpenTelemetry instance at all and registers the OtlpMeterRegistry based on a configuration. Currently I have no way of specifying a OtlpGrpcMetricExporter that it want to use.
My proposition is to have separate basic OpenTelemetry auto-configuration, a OpenTelemetry tracing auto-configuration and finally a OpenTelemetry metrics auto-configuration.
The main OpenTeleletry autoconfiguration would have the following:
@Bean
@ConditionalOnMissingBean
// puts the sevice name based on the spring.application.name, additionally reads in additional attributes like OtlpMetricsExportAutoConfiguration does
fun otelResource(environment: Environment): Resource {
val applicationName = environment.getProperty("spring.application.name", "application")
return Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName))
}
@Bean
@ConditionalOnMissingBean
fun openTelemetry(
sdkTracerProvider: ObjectProvider<SdkTracerProvider>,
sdkMeterProvider: ObjectProvider<SdkMeterProvider>, // This is the new bit - custom SdkMeterProvider that is set up like SdkTracerProvider allowing me to create a OtlpGrpcMetricExporter bean
contextPropagators: ObjectProvider<ContextPropagators>
): OpenTelemetry {
val builder = OpenTelemetrySdk.builder()
contextPropagators.ifUnique {
builder.setPropagators(it)
}
sdkMeterProvider.ifUnique {
builder.setMeterProvider(it)
}
sdkTracerProvider.ifUnique {
// set by org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.otelSdkTracerProvider
builder.setTracerProvider(it)
}
return builder.build()
}
The otelResource is a consistent way to specify the service name and other parameters for both metrics and tracing. The OpenTelemetry accepts the additional sdkMeterProvider and allows a setup of a custom MetricExporter.
The OtlpMetricsExportAutoConfiguration would create the SdkMeterProvider, Exporter and Registry:
@Bean
fun sdkMeterProvider(
metricExportersProvider: ObjectProvider<List<MetricExporter>>,
otelResource: Resource
): SdkMeterProvider {
// from https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java
val meterProviderBuilder = SdkMeterProvider.builder()
val interval = properties.metrics.interval
metricExportersProvider.getIfAvailable { emptyList() }
.map { metricExporter ->
val metricReaderBuilder = PeriodicMetricReader.builder(metricExporter)
if (interval != null) {
metricReaderBuilder.setInterval(interval)
}
metricReaderBuilder.build()
}
.forEach { reader ->
meterProviderBuilder.registerMetricReader(reader)
}
return meterProviderBuilder.setResource(otelResource).build()
}
@Bean
fun otelMeterRegistry(openTelemetry: OpenTelemetry): MeterRegistry {
return OpenTelemetryMeterRegistry.create(openTelemetry)
}
@Bean
@ConditionalOnMissingBean(MetricExporter::class)
fun metricExporter(...) : OtlpHttpMetricExporter {
// ...
}
So now both tracing and metrics use the same OpenTelemetry instance that is set up with a single OpenTelemetry Resource instance. The default metric exporter is the OtlpHttpMetricExporter and can be overridden with the GRPC version.
Hi!
I would like to propose an improvement in the way the OpenTelemetry metrics and tracing are auto-configured.
This is related to the issue: #30156 and my comment at the end.
As a starting point, the tracing
OpenTelemetryAutoConfigurationsets up an instance of the OpenTelemetry and fetches a list of tracing providers. It is a great way to specify a non-supported exporter (e.g.OtlpGrpcSpanExporterinstead of brave/zipkin/wavefront).But the
OtlpMetricsExportAutoConfigurationdoes not use the OpenTelemetry instance at all and registers theOtlpMeterRegistrybased on a configuration. Currently I have no way of specifying aOtlpGrpcMetricExporterthat it want to use.My proposition is to have separate basic OpenTelemetry auto-configuration, a OpenTelemetry tracing auto-configuration and finally a OpenTelemetry metrics auto-configuration.
The main OpenTeleletry autoconfiguration would have the following:
@Bean @ConditionalOnMissingBean // puts the sevice name based on the spring.application.name, additionally reads in additional attributes like OtlpMetricsExportAutoConfiguration does fun otelResource(environment: Environment): Resource { val applicationName = environment.getProperty("spring.application.name", "application") return Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)) } @Bean @ConditionalOnMissingBean fun openTelemetry( sdkTracerProvider: ObjectProvider<SdkTracerProvider>, sdkMeterProvider: ObjectProvider<SdkMeterProvider>, // This is the new bit - custom SdkMeterProvider that is set up like SdkTracerProvider allowing me to create a OtlpGrpcMetricExporter bean contextPropagators: ObjectProvider<ContextPropagators> ): OpenTelemetry { val builder = OpenTelemetrySdk.builder() contextPropagators.ifUnique { builder.setPropagators(it) } sdkMeterProvider.ifUnique { builder.setMeterProvider(it) } sdkTracerProvider.ifUnique { // set by org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.otelSdkTracerProvider builder.setTracerProvider(it) } return builder.build() }The
otelResourceis a consistent way to specify the service name and other parameters for both metrics and tracing. The OpenTelemetry accepts the additionalsdkMeterProviderand allows a setup of a customMetricExporter.The
OtlpMetricsExportAutoConfigurationwould create theSdkMeterProvider, Exporter and Registry:@Bean fun sdkMeterProvider( metricExportersProvider: ObjectProvider<List<MetricExporter>>, otelResource: Resource ): SdkMeterProvider { // from https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/OpenTelemetryAutoConfiguration.java val meterProviderBuilder = SdkMeterProvider.builder() val interval = properties.metrics.interval metricExportersProvider.getIfAvailable { emptyList() } .map { metricExporter -> val metricReaderBuilder = PeriodicMetricReader.builder(metricExporter) if (interval != null) { metricReaderBuilder.setInterval(interval) } metricReaderBuilder.build() } .forEach { reader -> meterProviderBuilder.registerMetricReader(reader) } return meterProviderBuilder.setResource(otelResource).build() } @Bean fun otelMeterRegistry(openTelemetry: OpenTelemetry): MeterRegistry { return OpenTelemetryMeterRegistry.create(openTelemetry) } @Bean @ConditionalOnMissingBean(MetricExporter::class) fun metricExporter(...) : OtlpHttpMetricExporter { // ... }So now both tracing and metrics use the same OpenTelemetry instance that is set up with a single OpenTelemetry Resource instance. The default metric exporter is the
OtlpHttpMetricExporterand can be overridden with the GRPC version.