Add missing OTel Span attributes
OTel semantic conventions mandate certain resource attributes to present on exported spans. This commits make sure that the attribute we add are merged with the defaults, rather than replacing them. See gh-36155
This commit is contained in:
parent
83457bb0e8
commit
58cfdbfa2d
|
|
@ -97,9 +97,10 @@ public class OpenTelemetryAutoConfiguration {
|
|||
SdkTracerProvider otelSdkTracerProvider(Environment environment, ObjectProvider<SpanProcessor> spanProcessors,
|
||||
Sampler sampler) {
|
||||
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.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
|
||||
.setResource(Resource.getDefault().merge(springResource));
|
||||
spanProcessors.orderedStream().forEach(builder::addSpanProcessor);
|
||||
return builder.build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.tracing;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.micrometer.tracing.SpanCustomizer;
|
||||
|
|
@ -30,18 +36,26 @@ 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.trace.Tracer;
|
||||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.extension.trace.propagation.B3Propagator;
|
||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.sdk.trace.SpanProcessor;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.export.SpanExporter;
|
||||
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
||||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
|
@ -143,6 +157,26 @@ class OpenTelemetryAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetupDefaultResourceAttributes() {
|
||||
this.contextRunner
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class))
|
||||
.withUserConfiguration(InMemoryRecordingSpanExporterConfiguration.class)
|
||||
.withPropertyValues("management.tracing.sampling.probability=1.0")
|
||||
.run((context) -> {
|
||||
context.getBean(io.micrometer.tracing.Tracer.class).nextSpan().name("test").end();
|
||||
InMemoryRecordingSpanExporter exporter = context.getBean(InMemoryRecordingSpanExporter.class);
|
||||
exporter.await(Duration.ofSeconds(10));
|
||||
SpanData spanData = exporter.getExportedSpans().get(0);
|
||||
Map<AttributeKey<?>, Object> expectedAttributes = Resource.getDefault()
|
||||
.merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "application")))
|
||||
.getAttributes()
|
||||
.asMap();
|
||||
assertThat(spanData.getResource().getAttributes().asMap()).isEqualTo(expectedAttributes);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowMultipleSpanProcessors() {
|
||||
this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> {
|
||||
|
|
@ -297,4 +331,50 @@ class OpenTelemetryAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
private static class InMemoryRecordingSpanExporterConfiguration {
|
||||
|
||||
@Bean
|
||||
InMemoryRecordingSpanExporter spanExporter() {
|
||||
return new InMemoryRecordingSpanExporter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class InMemoryRecordingSpanExporter implements SpanExporter {
|
||||
|
||||
private final List<SpanData> exportedSpans = new ArrayList<>();
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public CompletableResultCode export(Collection<SpanData> spans) {
|
||||
this.exportedSpans.addAll(spans);
|
||||
this.latch.countDown();
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode flush() {
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableResultCode shutdown() {
|
||||
this.exportedSpans.clear();
|
||||
return CompletableResultCode.ofSuccess();
|
||||
}
|
||||
|
||||
List<SpanData> getExportedSpans() {
|
||||
return this.exportedSpans;
|
||||
}
|
||||
|
||||
void await(Duration timeout) throws InterruptedException, TimeoutException {
|
||||
if (!this.latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
throw new TimeoutException("Waiting for exporting spans timed out (%s)".formatted(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue