|
16 | 16 |
|
17 | 17 | package org.springframework.boot.actuate.autoconfigure.tracing;
|
18 | 18 |
|
| 19 | +import java.time.Duration; |
19 | 20 | import java.util.ArrayList;
|
| 21 | +import java.util.Collection; |
20 | 22 | import java.util.List;
|
| 23 | +import java.util.Map; |
| 24 | +import java.util.concurrent.CountDownLatch; |
| 25 | +import java.util.concurrent.TimeUnit; |
| 26 | +import java.util.concurrent.TimeoutException; |
21 | 27 | import java.util.stream.Stream;
|
22 | 28 |
|
23 | 29 | import io.micrometer.tracing.SpanCustomizer;
|
|
30 | 36 | import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
|
31 | 37 | import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
|
32 | 38 | import io.opentelemetry.api.OpenTelemetry;
|
| 39 | +import io.opentelemetry.api.common.AttributeKey; |
| 40 | +import io.opentelemetry.api.common.Attributes; |
33 | 41 | import io.opentelemetry.api.trace.Tracer;
|
34 | 42 | import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
35 | 43 | import io.opentelemetry.context.propagation.ContextPropagators;
|
36 | 44 | import io.opentelemetry.context.propagation.TextMapPropagator;
|
37 | 45 | import io.opentelemetry.extension.trace.propagation.B3Propagator;
|
| 46 | +import io.opentelemetry.sdk.common.CompletableResultCode; |
| 47 | +import io.opentelemetry.sdk.resources.Resource; |
38 | 48 | import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
39 | 49 | import io.opentelemetry.sdk.trace.SpanProcessor;
|
| 50 | +import io.opentelemetry.sdk.trace.data.SpanData; |
| 51 | +import io.opentelemetry.sdk.trace.export.SpanExporter; |
40 | 52 | import io.opentelemetry.sdk.trace.samplers.Sampler;
|
| 53 | +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; |
41 | 54 | import org.junit.jupiter.api.Test;
|
42 | 55 | import org.junit.jupiter.params.ParameterizedTest;
|
43 | 56 | import org.junit.jupiter.params.provider.ValueSource;
|
44 | 57 |
|
| 58 | +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; |
45 | 59 | import org.springframework.boot.autoconfigure.AutoConfigurations;
|
46 | 60 | import org.springframework.boot.test.context.FilteredClassLoader;
|
47 | 61 | import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
@@ -143,6 +157,26 @@ void shouldBackOffOnCustomBeans() {
|
143 | 157 | });
|
144 | 158 | }
|
145 | 159 |
|
| 160 | + @Test |
| 161 | + void shouldSetupDefaultResourceAttributes() { |
| 162 | + this.contextRunner |
| 163 | + .withConfiguration( |
| 164 | + AutoConfigurations.of(ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class)) |
| 165 | + .withUserConfiguration(InMemoryRecordingSpanExporterConfiguration.class) |
| 166 | + .withPropertyValues("management.tracing.sampling.probability=1.0") |
| 167 | + .run((context) -> { |
| 168 | + context.getBean(io.micrometer.tracing.Tracer.class).nextSpan().name("test").end(); |
| 169 | + InMemoryRecordingSpanExporter exporter = context.getBean(InMemoryRecordingSpanExporter.class); |
| 170 | + exporter.await(Duration.ofSeconds(10)); |
| 171 | + SpanData spanData = exporter.getExportedSpans().get(0); |
| 172 | + Map<AttributeKey<?>, Object> expectedAttributes = Resource.getDefault() |
| 173 | + .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "application"))) |
| 174 | + .getAttributes() |
| 175 | + .asMap(); |
| 176 | + assertThat(spanData.getResource().getAttributes().asMap()).isEqualTo(expectedAttributes); |
| 177 | + }); |
| 178 | + } |
| 179 | + |
146 | 180 | @Test
|
147 | 181 | void shouldAllowMultipleSpanProcessors() {
|
148 | 182 | this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> {
|
@@ -297,4 +331,50 @@ SpanCustomizer customSpanCustomizer() {
|
297 | 331 |
|
298 | 332 | }
|
299 | 333 |
|
| 334 | + @Configuration(proxyBeanMethods = false) |
| 335 | + private static class InMemoryRecordingSpanExporterConfiguration { |
| 336 | + |
| 337 | + @Bean |
| 338 | + InMemoryRecordingSpanExporter spanExporter() { |
| 339 | + return new InMemoryRecordingSpanExporter(); |
| 340 | + } |
| 341 | + |
| 342 | + } |
| 343 | + |
| 344 | + private static class InMemoryRecordingSpanExporter implements SpanExporter { |
| 345 | + |
| 346 | + private final List<SpanData> exportedSpans = new ArrayList<>(); |
| 347 | + |
| 348 | + private final CountDownLatch latch = new CountDownLatch(1); |
| 349 | + |
| 350 | + @Override |
| 351 | + public CompletableResultCode export(Collection<SpanData> spans) { |
| 352 | + this.exportedSpans.addAll(spans); |
| 353 | + this.latch.countDown(); |
| 354 | + return CompletableResultCode.ofSuccess(); |
| 355 | + } |
| 356 | + |
| 357 | + @Override |
| 358 | + public CompletableResultCode flush() { |
| 359 | + return CompletableResultCode.ofSuccess(); |
| 360 | + } |
| 361 | + |
| 362 | + @Override |
| 363 | + public CompletableResultCode shutdown() { |
| 364 | + this.exportedSpans.clear(); |
| 365 | + return CompletableResultCode.ofSuccess(); |
| 366 | + } |
| 367 | + |
| 368 | + List<SpanData> getExportedSpans() { |
| 369 | + return this.exportedSpans; |
| 370 | + } |
| 371 | + |
| 372 | + void await(Duration timeout) throws InterruptedException, TimeoutException { |
| 373 | + if (!this.latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) { |
| 374 | + throw new TimeoutException("Waiting for exporting spans timed out (%s)".formatted(timeout)); |
| 375 | + } |
| 376 | + } |
| 377 | + |
| 378 | + } |
| 379 | + |
300 | 380 | }
|
0 commit comments