|
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.SpanLimits;
|
40 | 50 | import io.opentelemetry.sdk.trace.SpanProcessor;
|
| 51 | +import io.opentelemetry.sdk.trace.data.SpanData; |
| 52 | +import io.opentelemetry.sdk.trace.export.SpanExporter; |
41 | 53 | import io.opentelemetry.sdk.trace.samplers.Sampler;
|
| 54 | +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; |
42 | 55 | import org.junit.jupiter.api.Test;
|
43 | 56 | import org.junit.jupiter.params.ParameterizedTest;
|
44 | 57 | import org.junit.jupiter.params.provider.ValueSource;
|
45 | 58 |
|
| 59 | +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; |
46 | 60 | import org.springframework.boot.autoconfigure.AutoConfigurations;
|
47 | 61 | import org.springframework.boot.test.context.FilteredClassLoader;
|
48 | 62 | import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
@@ -146,6 +160,26 @@ void shouldBackOffOnCustomBeans() {
|
146 | 160 | });
|
147 | 161 | }
|
148 | 162 |
|
| 163 | + @Test |
| 164 | + void shouldSetupDefaultResourceAttributes() { |
| 165 | + this.contextRunner |
| 166 | + .withConfiguration( |
| 167 | + AutoConfigurations.of(ObservationAutoConfiguration.class, MicrometerTracingAutoConfiguration.class)) |
| 168 | + .withUserConfiguration(InMemoryRecordingSpanExporterConfiguration.class) |
| 169 | + .withPropertyValues("management.tracing.sampling.probability=1.0") |
| 170 | + .run((context) -> { |
| 171 | + context.getBean(io.micrometer.tracing.Tracer.class).nextSpan().name("test").end(); |
| 172 | + InMemoryRecordingSpanExporter exporter = context.getBean(InMemoryRecordingSpanExporter.class); |
| 173 | + exporter.await(Duration.ofSeconds(10)); |
| 174 | + SpanData spanData = exporter.getExportedSpans().get(0); |
| 175 | + Map<AttributeKey<?>, Object> expectedAttributes = Resource.getDefault() |
| 176 | + .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "application"))) |
| 177 | + .getAttributes() |
| 178 | + .asMap(); |
| 179 | + assertThat(spanData.getResource().getAttributes().asMap()).isEqualTo(expectedAttributes); |
| 180 | + }); |
| 181 | + } |
| 182 | + |
149 | 183 | @Test
|
150 | 184 | void shouldAllowMultipleSpanProcessors() {
|
151 | 185 | this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> {
|
@@ -332,4 +366,50 @@ SdkTracerProviderBuilderCustomizer sdkTracerProviderBuilderCustomizerTwo() {
|
332 | 366 |
|
333 | 367 | }
|
334 | 368 |
|
| 369 | + @Configuration(proxyBeanMethods = false) |
| 370 | + private static class InMemoryRecordingSpanExporterConfiguration { |
| 371 | + |
| 372 | + @Bean |
| 373 | + InMemoryRecordingSpanExporter spanExporter() { |
| 374 | + return new InMemoryRecordingSpanExporter(); |
| 375 | + } |
| 376 | + |
| 377 | + } |
| 378 | + |
| 379 | + private static class InMemoryRecordingSpanExporter implements SpanExporter { |
| 380 | + |
| 381 | + private final List<SpanData> exportedSpans = new ArrayList<>(); |
| 382 | + |
| 383 | + private final CountDownLatch latch = new CountDownLatch(1); |
| 384 | + |
| 385 | + @Override |
| 386 | + public CompletableResultCode export(Collection<SpanData> spans) { |
| 387 | + this.exportedSpans.addAll(spans); |
| 388 | + this.latch.countDown(); |
| 389 | + return CompletableResultCode.ofSuccess(); |
| 390 | + } |
| 391 | + |
| 392 | + @Override |
| 393 | + public CompletableResultCode flush() { |
| 394 | + return CompletableResultCode.ofSuccess(); |
| 395 | + } |
| 396 | + |
| 397 | + @Override |
| 398 | + public CompletableResultCode shutdown() { |
| 399 | + this.exportedSpans.clear(); |
| 400 | + return CompletableResultCode.ofSuccess(); |
| 401 | + } |
| 402 | + |
| 403 | + List<SpanData> getExportedSpans() { |
| 404 | + return this.exportedSpans; |
| 405 | + } |
| 406 | + |
| 407 | + void await(Duration timeout) throws InterruptedException, TimeoutException { |
| 408 | + if (!this.latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)) { |
| 409 | + throw new TimeoutException("Waiting for exporting spans timed out (%s)".formatted(timeout)); |
| 410 | + } |
| 411 | + } |
| 412 | + |
| 413 | + } |
| 414 | + |
335 | 415 | }
|
0 commit comments