Split OpenTelemetry auto-configuration
The OpenTelemetry bean is now configured in the OpenTelemetryAutoConfiguration. This method also applies SdkLoggerProvider and SdkMeterProvider. Additionally, the OpenTelemetry Resource is now a bean. Resource attributes can now be configured through properties The resourceAttributes in OtlpProperties have been deprecated in favor of the new one in OpenTelemetryProperties. Closes gh-36544 Closes gh-36545
This commit is contained in:
parent
9cb5763794
commit
b0615dd311
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,6 +24,7 @@ import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegi
|
|||
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
|
@ -36,6 +37,7 @@ import org.springframework.context.annotation.Bean;
|
|||
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to OTLP.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@AutoConfiguration(
|
||||
|
@ -44,19 +46,23 @@ import org.springframework.context.annotation.Bean;
|
|||
@ConditionalOnBean(Clock.class)
|
||||
@ConditionalOnClass(OtlpMeterRegistry.class)
|
||||
@ConditionalOnEnabledMetricsExport("otlp")
|
||||
@EnableConfigurationProperties(OtlpProperties.class)
|
||||
@EnableConfigurationProperties({ OtlpProperties.class, OpenTelemetryProperties.class })
|
||||
public class OtlpMetricsExportAutoConfiguration {
|
||||
|
||||
private final OtlpProperties properties;
|
||||
|
||||
public OtlpMetricsExportAutoConfiguration(OtlpProperties properties) {
|
||||
private final OpenTelemetryProperties openTelemetryProperties;
|
||||
|
||||
public OtlpMetricsExportAutoConfiguration(OtlpProperties properties,
|
||||
OpenTelemetryProperties openTelemetryProperties) {
|
||||
this.properties = properties;
|
||||
this.openTelemetryProperties = openTelemetryProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public OtlpConfig otlpConfig() {
|
||||
return new OtlpPropertiesConfigAdapter(this.properties);
|
||||
return new OtlpPropertiesConfigAdapter(this.properties, this.openTelemetryProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -23,6 +23,7 @@ import io.micrometer.registry.otlp.AggregationTemporality;
|
|||
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties @ConfigurationProperties} for configuring OTLP metrics
|
||||
|
@ -77,6 +78,7 @@ public class OtlpProperties extends StepRegistryProperties {
|
|||
this.aggregationTemporality = aggregationTemporality;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "management.opentelemetry.resource-attributes")
|
||||
public Map<String, String> getResourceAttributes() {
|
||||
return this.resourceAttributes;
|
||||
}
|
||||
|
|
|
@ -23,17 +23,23 @@ import io.micrometer.registry.otlp.AggregationTemporality;
|
|||
import io.micrometer.registry.otlp.OtlpConfig;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Adapter to convert {@link OtlpProperties} to an {@link OtlpConfig}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Jonatan Ivanov
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<OtlpProperties> implements OtlpConfig {
|
||||
|
||||
OtlpPropertiesConfigAdapter(OtlpProperties properties) {
|
||||
private final OpenTelemetryProperties openTelemetryProperties;
|
||||
|
||||
OtlpPropertiesConfigAdapter(OtlpProperties properties, OpenTelemetryProperties openTelemetryProperties) {
|
||||
super(properties);
|
||||
this.openTelemetryProperties = openTelemetryProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,6 +59,9 @@ class OtlpPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<Ot
|
|||
|
||||
@Override
|
||||
public Map<String, String> resourceAttributes() {
|
||||
if (!CollectionUtils.isEmpty(this.openTelemetryProperties.getResourceAttributes())) {
|
||||
return this.openTelemetryProperties.getResourceAttributes();
|
||||
}
|
||||
return get(OtlpProperties::getResourceAttributes, OtlpConfig.super::resourceAttributes);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdkBuilder;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(OpenTelemetrySdk.class)
|
||||
@EnableConfigurationProperties(OpenTelemetryProperties.class)
|
||||
public class OpenTelemetryAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Default value for application name if {@code spring.application.name} is not set.
|
||||
*/
|
||||
private static final String DEFAULT_APPLICATION_NAME = "application";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OpenTelemetry.class)
|
||||
OpenTelemetrySdk openTelemetry(ObjectProvider<SdkTracerProvider> tracerProvider,
|
||||
ObjectProvider<ContextPropagators> propagators, ObjectProvider<SdkLoggerProvider> loggerProvider,
|
||||
ObjectProvider<SdkMeterProvider> meterProvider) {
|
||||
OpenTelemetrySdkBuilder builder = OpenTelemetrySdk.builder();
|
||||
tracerProvider.ifAvailable(builder::setTracerProvider);
|
||||
propagators.ifAvailable(builder::setPropagators);
|
||||
loggerProvider.ifAvailable(builder::setLoggerProvider);
|
||||
meterProvider.ifAvailable(builder::setMeterProvider);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
Resource openTelemetryResource(Environment environment, OpenTelemetryProperties properties) {
|
||||
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
|
||||
return Resource.getDefault()
|
||||
.merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)))
|
||||
.merge(properties.toResource());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.resources.ResourceBuilder;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for OpenTelemetry.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "management.opentelemetry")
|
||||
public class OpenTelemetryProperties {
|
||||
|
||||
/**
|
||||
* Resource attributes.
|
||||
*/
|
||||
private Map<String, String> resourceAttributes = new HashMap<>();
|
||||
|
||||
public Map<String, String> getResourceAttributes() {
|
||||
return this.resourceAttributes;
|
||||
}
|
||||
|
||||
public void setResourceAttributes(Map<String, String> resourceAttributes) {
|
||||
this.resourceAttributes = resourceAttributes;
|
||||
}
|
||||
|
||||
Resource toResource() {
|
||||
ResourceBuilder builder = Resource.builder();
|
||||
for (Entry<String, String> entry : this.resourceAttributes.entrySet()) {
|
||||
builder.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for OpenTelemetry.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
|
@ -36,13 +36,11 @@ import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
|
|||
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
|
||||
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.MeterProvider;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.ContextStorage;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
|
||||
|
@ -51,7 +49,6 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
|
|||
import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder;
|
||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
||||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.SpringBootVersion;
|
||||
|
@ -63,26 +60,20 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|||
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;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry tracing.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Yanming Zhou
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@AutoConfiguration(before = MicrometerTracingAutoConfiguration.class)
|
||||
@AutoConfiguration(value = "openTelemetryTracingAutoConfiguration", before = MicrometerTracingAutoConfiguration.class)
|
||||
@ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class })
|
||||
@EnableConfigurationProperties(TracingProperties.class)
|
||||
public class OpenTelemetryAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Default value for application name if {@code spring.application.name} is not set.
|
||||
*/
|
||||
private static final String DEFAULT_APPLICATION_NAME = "application";
|
||||
|
||||
private final TracingProperties tracingProperties;
|
||||
|
||||
OpenTelemetryAutoConfiguration(TracingProperties tracingProperties) {
|
||||
|
@ -91,22 +82,9 @@ public class OpenTelemetryAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
OpenTelemetry openTelemetry(SdkTracerProvider sdkTracerProvider, ContextPropagators contextPropagators) {
|
||||
return OpenTelemetrySdk.builder()
|
||||
.setTracerProvider(sdkTracerProvider)
|
||||
.setPropagators(contextPropagators)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SdkTracerProvider otelSdkTracerProvider(Environment environment, SpanProcessors spanProcessors, Sampler sampler,
|
||||
SdkTracerProvider otelSdkTracerProvider(Resource resource, SpanProcessors spanProcessors, Sampler sampler,
|
||||
ObjectProvider<SdkTracerProviderBuilderCustomizer> customizers) {
|
||||
String applicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
|
||||
Resource springResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName));
|
||||
SdkTracerProviderBuilder builder = SdkTracerProvider.builder()
|
||||
.setSampler(sampler)
|
||||
.setResource(Resource.getDefault().merge(springResource));
|
||||
SdkTracerProviderBuilder builder = SdkTracerProvider.builder().setSampler(sampler).setResource(resource);
|
||||
spanProcessors.forEach(builder::addSpanProcessor);
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
|
|
|
@ -88,6 +88,7 @@ org.springframework.boot.actuate.autoconfigure.data.mongo.MongoReactiveHealthCon
|
|||
org.springframework.boot.actuate.autoconfigure.neo4j.Neo4jHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.quartz.QuartzEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.r2dbc.ConnectionFactoryHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.r2dbc.R2dbcObservationAutoConfiguration
|
||||
|
|
|
@ -16,69 +16,93 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.micrometer.registry.otlp.AggregationTemporality;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link OtlpPropertiesConfigAdapter}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class OtlpPropertiesConfigAdapterTests {
|
||||
|
||||
private OtlpProperties properties;
|
||||
|
||||
private OpenTelemetryProperties openTelemetryProperties;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.properties = new OtlpProperties();
|
||||
this.openTelemetryProperties = new OpenTelemetryProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesUrlIsSetAdapterUrlReturnsIt() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
properties.setUrl("http://another-url:4318/v1/metrics");
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).url()).isEqualTo("http://another-url:4318/v1/metrics");
|
||||
this.properties.setUrl("http://another-url:4318/v1/metrics");
|
||||
assertThat(createAdapter().url()).isEqualTo("http://another-url:4318/v1/metrics");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesAggregationTemporalityIsNotSetAdapterAggregationTemporalityReturnsCumulative() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).aggregationTemporality())
|
||||
.isSameAs(AggregationTemporality.CUMULATIVE);
|
||||
assertThat(createAdapter().aggregationTemporality()).isSameAs(AggregationTemporality.CUMULATIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesAggregationTemporalityIsSetAdapterAggregationTemporalityReturnsIt() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
properties.setAggregationTemporality(AggregationTemporality.DELTA);
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).aggregationTemporality())
|
||||
.isSameAs(AggregationTemporality.DELTA);
|
||||
this.properties.setAggregationTemporality(AggregationTemporality.DELTA);
|
||||
assertThat(createAdapter().aggregationTemporality()).isSameAs(AggregationTemporality.DELTA);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesResourceAttributesIsSetAdapterResourceAttributesReturnsIt() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
properties.setResourceAttributes(Map.of("service.name", "boot-service"));
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).resourceAttributes()).containsEntry("service.name",
|
||||
"boot-service");
|
||||
this.properties.setResourceAttributes(Map.of("service.name", "boot-service"));
|
||||
assertThat(createAdapter().resourceAttributes()).containsEntry("service.name", "boot-service");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesHeadersIsSetAdapterHeadersReturnsIt() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
properties.setHeaders(Map.of("header", "value"));
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).headers()).containsEntry("header", "value");
|
||||
this.properties.setHeaders(Map.of("header", "value"));
|
||||
assertThat(createAdapter().headers()).containsEntry("header", "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesBaseTimeUnitIsNotSetAdapterBaseTimeUnitReturnsMillis() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).baseTimeUnit()).isSameAs(TimeUnit.MILLISECONDS);
|
||||
assertThat(createAdapter().baseTimeUnit()).isSameAs(TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenPropertiesBaseTimeUnitIsSetAdapterBaseTimeUnitReturnsIt() {
|
||||
OtlpProperties properties = new OtlpProperties();
|
||||
properties.setBaseTimeUnit(TimeUnit.SECONDS);
|
||||
assertThat(new OtlpPropertiesConfigAdapter(properties).baseTimeUnit()).isSameAs(TimeUnit.SECONDS);
|
||||
this.properties.setBaseTimeUnit(TimeUnit.SECONDS);
|
||||
assertThat(createAdapter().baseTimeUnit()).isSameAs(TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void openTelemetryPropertiesShouldOverrideOtlpPropertiesIfNotEmpty() {
|
||||
this.properties.setResourceAttributes(Map.of("a", "alpha"));
|
||||
this.openTelemetryProperties.setResourceAttributes(Map.of("b", "beta"));
|
||||
assertThat(createAdapter().resourceAttributes()).containsExactly(entry("b", "beta"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void openTelemetryPropertiesShouldNotOverrideOtlpPropertiesIfEmpty() {
|
||||
this.properties.setResourceAttributes(Map.of("a", "alpha"));
|
||||
this.openTelemetryProperties.setResourceAttributes(Collections.emptyMap());
|
||||
assertThat(createAdapter().resourceAttributes()).containsExactly(entry("a", "alpha"));
|
||||
}
|
||||
|
||||
private OtlpPropertiesConfigAdapter createAdapter() {
|
||||
return new OtlpPropertiesConfigAdapter(this.properties, this.openTelemetryProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.opentelemetry;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link OpenTelemetryAutoConfiguration}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class OpenTelemetryAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void isRegisteredInAutoConfigurationImports() {
|
||||
assertThat(ImportCandidates.load(AutoConfiguration.class, null).getCandidates())
|
||||
.contains(OpenTelemetryAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProvideBeans() {
|
||||
this.runner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffIfOpenTelemetryIsNotOnClasspath() {
|
||||
this.runner.withClassLoader(new FilteredClassLoader("io.opentelemetry")).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetrySdk.class);
|
||||
assertThat(context).doesNotHaveBean(Resource.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffOnUserSuppliedBeans() {
|
||||
this.runner.withUserConfiguration(UserConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(OpenTelemetry.class);
|
||||
assertThat(context).hasBean("customOpenTelemetry");
|
||||
assertThat(context).hasSingleBean(Resource.class);
|
||||
assertThat(context).hasBean("customResource");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplySpringApplicationNameToResource() {
|
||||
this.runner.withPropertyValues("spring.application.name=my-application").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(ResourceAttributes.SERVICE_NAME, "my-application"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallbackToDefaultApplicationNameIfSpringApplicationNameIsNotSet() {
|
||||
this.runner.run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap())
|
||||
.contains(entry(ResourceAttributes.SERVICE_NAME, "application"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplyResourceAttributesFromProperties() {
|
||||
this.runner.withPropertyValues("management.opentelemetry.resource-attributes.region=us-west").run((context) -> {
|
||||
Resource resource = context.getBean(Resource.class);
|
||||
assertThat(resource.getAttributes().asMap()).contains(entry(AttributeKey.stringKey("region"), "us-west"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterSdkTracerProviderIfAvailable() {
|
||||
this.runner.withBean(SdkTracerProvider.class, () -> SdkTracerProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getTracerProvider()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterContextPropagatorsIfAvailable() {
|
||||
this.runner.withBean(ContextPropagators.class, ContextPropagators::noop).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getPropagators()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterSdkLoggerProviderIfAvailable() {
|
||||
this.runner.withBean(SdkLoggerProvider.class, () -> SdkLoggerProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getLogsBridge()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterSdkMeterProviderIfAvailable() {
|
||||
this.runner.withBean(SdkMeterProvider.class, () -> SdkMeterProvider.builder().build()).run((context) -> {
|
||||
OpenTelemetry openTelemetry = context.getBean(OpenTelemetry.class);
|
||||
assertThat(openTelemetry.getMeterProvider()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
private static class UserConfiguration {
|
||||
|
||||
@Bean
|
||||
OpenTelemetry customOpenTelemetry() {
|
||||
return mock(OpenTelemetry.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Resource customResource() {
|
||||
return Resource.getDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -151,8 +152,9 @@ class BaggagePropagationIntegrationTests {
|
|||
OTEL_DEFAULT {
|
||||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
|
||||
return new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(
|
||||
OpenTelemetryAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
"management.tracing.baggage.correlation.fields=country-code,bp");
|
||||
}
|
||||
|
@ -172,8 +174,9 @@ class BaggagePropagationIntegrationTests {
|
|||
OTEL_W3C {
|
||||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
|
||||
return new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(
|
||||
OpenTelemetryAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.propagation.type=W3C",
|
||||
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
"management.tracing.baggage.correlation.fields=country-code,bp");
|
||||
|
@ -205,8 +208,9 @@ class BaggagePropagationIntegrationTests {
|
|||
OTEL_B3 {
|
||||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
|
||||
return new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(
|
||||
OpenTelemetryAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.propagation.type=B3",
|
||||
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
"management.tracing.baggage.correlation.fields=country-code,bp");
|
||||
|
@ -216,8 +220,9 @@ class BaggagePropagationIntegrationTests {
|
|||
OTEL_B3_MULTI {
|
||||
@Override
|
||||
public ApplicationContextRunner get() {
|
||||
return new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
|
||||
return new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(
|
||||
OpenTelemetryAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class))
|
||||
.withPropertyValues("management.tracing.propagation.type=B3_MULTI",
|
||||
"management.tracing.baggage.remote-fields=x-vcap-request-id,country-code,bp",
|
||||
"management.tracing.baggage.correlation.fields=country-code,bp");
|
||||
|
|
|
@ -35,7 +35,6 @@ import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher;
|
|||
import io.micrometer.tracing.otel.bridge.Slf4JBaggageEventListener;
|
||||
import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
|
||||
import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.MeterProvider;
|
||||
|
@ -83,7 +82,9 @@ import static org.mockito.Mockito.mock;
|
|||
class OpenTelemetryAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration.class,
|
||||
OpenTelemetryAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void shouldSupplyBeans() {
|
||||
|
@ -91,7 +92,6 @@ class OpenTelemetryAutoConfigurationTests {
|
|||
assertThat(context).hasSingleBean(OtelTracer.class);
|
||||
assertThat(context).hasSingleBean(EventPublisher.class);
|
||||
assertThat(context).hasSingleBean(OtelCurrentTraceContext.class);
|
||||
assertThat(context).hasSingleBean(OpenTelemetry.class);
|
||||
assertThat(context).hasSingleBean(SdkTracerProvider.class);
|
||||
assertThat(context).hasSingleBean(ContextPropagators.class);
|
||||
assertThat(context).hasSingleBean(Sampler.class);
|
||||
|
@ -123,7 +123,6 @@ class OpenTelemetryAutoConfigurationTests {
|
|||
assertThat(context).doesNotHaveBean(OtelTracer.class);
|
||||
assertThat(context).doesNotHaveBean(EventPublisher.class);
|
||||
assertThat(context).doesNotHaveBean(OtelCurrentTraceContext.class);
|
||||
assertThat(context).doesNotHaveBean(OpenTelemetry.class);
|
||||
assertThat(context).doesNotHaveBean(SdkTracerProvider.class);
|
||||
assertThat(context).doesNotHaveBean(ContextPropagators.class);
|
||||
assertThat(context).doesNotHaveBean(Sampler.class);
|
||||
|
@ -148,8 +147,6 @@ class OpenTelemetryAutoConfigurationTests {
|
|||
assertThat(context).hasSingleBean(EventPublisher.class);
|
||||
assertThat(context).hasBean("customOtelCurrentTraceContext");
|
||||
assertThat(context).hasSingleBean(OtelCurrentTraceContext.class);
|
||||
assertThat(context).hasBean("customOpenTelemetry");
|
||||
assertThat(context).hasSingleBean(OpenTelemetry.class);
|
||||
assertThat(context).hasBean("customSdkTracerProvider");
|
||||
assertThat(context).hasSingleBean(SdkTracerProvider.class);
|
||||
assertThat(context).hasBean("customContextPropagators");
|
||||
|
@ -369,11 +366,6 @@ class OpenTelemetryAutoConfigurationTests {
|
|||
return mock(OtelCurrentTraceContext.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
OpenTelemetry customOpenTelemetry() {
|
||||
return mock(OpenTelemetry.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SdkTracerProvider customSdkTracerProvider() {
|
||||
return SdkTracerProvider.builder().build();
|
||||
|
|
|
@ -34,8 +34,8 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
|
@ -50,9 +50,10 @@ class OtlpAutoConfigurationIntegrationTests {
|
|||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("management.tracing.sampling.probability=1.0")
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class,
|
||||
OpenTelemetryAutoConfiguration.class, OtlpAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(ObservationAutoConfiguration.class,
|
||||
MicrometerTracingAutoConfiguration.class, OpenTelemetryAutoConfiguration.class,
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfiguration.class,
|
||||
OtlpAutoConfiguration.class));
|
||||
|
||||
private final MockWebServer mockWebServer = new MockWebServer();
|
||||
|
||||
|
|
|
@ -67,4 +67,15 @@ include::code:MyObservationPredicate[]
|
|||
|
||||
The preceding example will prevent all observations whose name contains "denied".
|
||||
|
||||
[[actuator.observability.opentelemetry]]
|
||||
=== OpenTelemetry Support
|
||||
Spring Boot's actuator module includes basic support for https://opentelemetry.io/[OpenTelemetry].
|
||||
|
||||
It provides a bean of type `OpenTelemetry`, and if there are beans of type `SdkTracerProvider`, `ContextPropagators`, `SdkLoggerProvider` or `SdkMeterProvider` in the application context, they automatically get registered.
|
||||
Additionally, it provides a `Resource` bean.
|
||||
The attributes of the `Resource` can be configured via the configprop:management.opentelemetry.resource-attributes[] configuration property.
|
||||
|
||||
NOTE: Spring Boot does not provide auto-configuration for OpenTelemetry metrics or logging.
|
||||
OpenTelemetry tracing is only auto-configured when used together with <<actuator#actuator.micrometer-tracing, Micrometer Tracing>>.
|
||||
|
||||
The next sections will provide more details about logging, metrics and traces.
|
||||
|
|
Loading…
Reference in New Issue