|
16 | 16 |
|
17 | 17 | package org.springframework.boot.actuate.autoconfigure.tracing;
|
18 | 18 |
|
| 19 | +import java.time.Duration; |
19 | 20 | import java.util.ArrayList;
|
20 | 21 | import java.util.Collection;
|
21 | 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; |
22 | 27 | import java.util.stream.Stream;
|
23 | 28 |
|
24 | 29 | import io.micrometer.tracing.SpanCustomizer;
|
|
31 | 36 | import io.micrometer.tracing.otel.bridge.Slf4JEventListener;
|
32 | 37 | import io.micrometer.tracing.otel.propagation.BaggageTextMapPropagator;
|
33 | 38 | import io.opentelemetry.api.OpenTelemetry;
|
| 39 | +import io.opentelemetry.api.common.AttributeKey; |
| 40 | +import io.opentelemetry.api.common.Attributes; |
34 | 41 | import io.opentelemetry.api.metrics.MeterProvider;
|
35 | 42 | import io.opentelemetry.api.trace.Tracer;
|
36 | 43 | import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
37 | 44 | import io.opentelemetry.context.propagation.ContextPropagators;
|
38 | 45 | import io.opentelemetry.context.propagation.TextMapPropagator;
|
39 | 46 | import io.opentelemetry.extension.trace.propagation.B3Propagator;
|
40 | 47 | import io.opentelemetry.sdk.common.CompletableResultCode;
|
| 48 | +import io.opentelemetry.sdk.resources.Resource; |
41 | 49 | import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
42 | 50 | import io.opentelemetry.sdk.trace.SpanLimits;
|
43 | 51 | import io.opentelemetry.sdk.trace.SpanProcessor;
|
44 | 52 | import io.opentelemetry.sdk.trace.data.SpanData;
|
45 | 53 | import io.opentelemetry.sdk.trace.export.SpanExporter;
|
46 | 54 | import io.opentelemetry.sdk.trace.samplers.Sampler;
|
| 55 | +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; |
47 | 56 | import org.junit.jupiter.api.Test;
|
48 | 57 | import org.junit.jupiter.params.ParameterizedTest;
|
49 | 58 | import org.junit.jupiter.params.provider.ValueSource;
|
50 | 59 | import org.mockito.Mockito;
|
51 | 60 |
|
| 61 | +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; |
52 | 62 | import org.springframework.boot.autoconfigure.AutoConfigurations;
|
53 | 63 | import org.springframework.boot.test.context.FilteredClassLoader;
|
54 | 64 | import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
@@ -163,6 +173,26 @@ void shouldBackOffOnCustomBeans() {
|
163 | 173 | });
|
164 | 174 | }
|
165 | 175 |
|
| 176 | + @Test |
| 177 | + void shouldSetupDefaultResourceAttributes() { |
| 178 | + this.contextRunner |
| 179 | + .withConfiguration( |
| 180 | + AutoConfigurations.of(ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class)) |
| 181 | + .withUserConfiguration(InMemoryRecordingSpanExporterConfiguration.class) |
| 182 | + .withPropertyValues("management.tracing.sampling.probability=1.0") |
| 183 | + .run((context) -> { |
| 184 | + context.getBean(io.micrometer.tracing.Tracer.class).nextSpan().name("test").end(); |
| 185 | + InMemoryRecordingSpanExporter exporter = context.getBean(InMemoryRecordingSpanExporter.class); |
| 186 | + exporter.await(Duration.ofSeconds(10)); |
| 187 | + SpanData spanData = exporter.getExportedSpans().get(0); |
| 188 | + Map<AttributeKey<?>, Object> expectedAttributes = Resource.getDefault() |
| 189 | + .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "application"))) |
| 190 | + .getAttributes() |
| 191 | + .asMap(); |
| 192 | + assertThat(spanData.getResource().getAttributes().asMap()).isEqualTo(expectedAttributes); |
| 193 | + }); |
| 194 | + } |
| 195 | + |
166 | 196 | @Test
|
167 | 197 | void shouldAllowMultipleSpanProcessors() {
|
168 | 198 | this.contextRunner.withUserConfiguration(AdditionalSpanProcessorConfiguration.class).run((context) -> {
|
@@ -438,4 +468,50 @@ public CompletableResultCode shutdown() {
|
438 | 468 |
|
439 | 469 | }
|
440 | 470 |
|
| 471 | + @Configuration(proxyBeanMethods = false) |
| 472 | + private static class InMemoryRecordingSpanExporterConfiguration { |
| 473 | + |
| 474 | + @Bean |
| 475 | + InMemoryRecordingSpanExporter spanExporter() { |
| 476 | + return new InMemoryRecordingSpanExporter(); |
| 477 | + } |
| 478 | + |
| 479 | + } |
| 480 | + |
| 481 | + private static class InMemoryRecordingSpanExporter implements SpanExporter { |
| 482 | + |
| 483 | + private final List<SpanData> exportedSpans = new ArrayList<>(); |
| 484 | + |
| 485 | + private final CountDownLatch latch = new CountDownLatch(1); |
| 486 | + |
| 487 | + @Override |
| 488 | + public CompletableResultCode export(Collection<SpanData> spans) { |
| 489 | + this.exportedSpans.addAll(spans); |
| 490 | + this.latch.countDown(); |
| 491 | + return CompletableResultCode.ofSuccess(); |
| 492 | + } |
| 493 | + |
| 494 | + @Override |
| 495 | + public CompletableResultCode flush() { |
| 496 | + return CompletableResultCode.ofSuccess(); |
| 497 | + } |
| 498 | + |
| 499 | + @Override |
| 500 | + public CompletableResultCode shutdown() { |
| 501 | + this.exportedSpans.clear(); |
| 502 | + return CompletableResultCode.ofSuccess(); |
| 503 | + } |
| 504 | + |
| 505 | + List<SpanData> getExportedSpans() { |
| 506 | + return this.exportedSpans; |
| 507 | + } |
| 508 | + |
| 509 | + void await(Duration timeout) throws InterruptedException, TimeoutException { |
| 510 | + if (!this.latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) { |
| 511 | + throw new TimeoutException("Waiting for exporting spans timed out (%s)".formatted(timeout)); |
| 512 | + } |
| 513 | + } |
| 514 | + |
| 515 | + } |
| 516 | + |
441 | 517 | }
|
0 commit comments