Skip to content

Commit f1db2bb

Browse files
authored
chore: add showcase integration tests for OpenTelemetry parent context tracing (#14535)
Showcase end-to-end integration tests verifying parent trace context linking for gRPC, HTTP/JSON, and retry attempt spans. b/564460690
1 parent 54d2bc6 commit f1db2bb

1 file changed

Lines changed: 131 additions & 0 deletions

File tree

  • java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it

‎java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelTracing.java‎

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@
6868
import io.grpc.MethodDescriptor;
6969
import io.opentelemetry.api.GlobalOpenTelemetry;
7070
import io.opentelemetry.api.common.AttributeKey;
71+
import io.opentelemetry.api.trace.Span;
7172
import io.opentelemetry.api.trace.SpanKind;
73+
import io.opentelemetry.api.trace.Tracer;
74+
import io.opentelemetry.context.Scope;
7275
import io.opentelemetry.sdk.OpenTelemetrySdk;
7376
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
7477
import io.opentelemetry.sdk.trace.SdkTracerProvider;
@@ -129,6 +132,7 @@ class ITOtelTracing {
129132

130133
private InMemorySpanExporter spanExporter;
131134
private OpenTelemetrySdk openTelemetrySdk;
135+
private Tracer tracer;
132136

133137
@BeforeEach
134138
void setup() {
@@ -141,6 +145,7 @@ void setup() {
141145

142146
openTelemetrySdk =
143147
OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();
148+
tracer = openTelemetrySdk.getTracer("it-otel-tracing-test");
144149
}
145150

146151
@AfterEach
@@ -456,6 +461,69 @@ void testTracing_retry_grpc() throws Exception {
456461
assertThat(resendCounts).containsExactlyElementsIn(expectedCounts).inOrder();
457462
}
458463

464+
/**
465+
* Verifies that when an RPC is invoked within an active OpenTelemetry trace context, all attempt
466+
* spans (including the initial call and retries scheduled on background threads) properly link to
467+
* the parent span context using gRPC transport.
468+
*/
469+
@Test
470+
void testTracing_retry_grpc_linkedToParentContext() throws Exception {
471+
final int attempts = 5;
472+
final StatusCode.Code statusCode = StatusCode.Code.UNAVAILABLE;
473+
RetrySettings retrySettings =
474+
RetrySettings.newBuilder()
475+
.setTotalTimeout(org.threeten.bp.Duration.ofMillis(5000L))
476+
.setMaxAttempts(attempts)
477+
.build();
478+
479+
OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory(openTelemetrySdk);
480+
481+
EchoSettings grpcEchoSettings = createEchoSettings(false);
482+
EchoStubSettings.Builder grpcEchoSettingsBuilder =
483+
(EchoStubSettings.Builder) grpcEchoSettings.getStubSettings().toBuilder();
484+
grpcEchoSettingsBuilder
485+
.echoSettings()
486+
.setRetrySettings(retrySettings)
487+
.setRetryableCodes(statusCode);
488+
grpcEchoSettingsBuilder.setTracerFactory(tracingFactory);
489+
EchoStub stub = new ExtendedEchoStubSettings(grpcEchoSettingsBuilder).createStub();
490+
EchoClient grpcClient = EchoClient.create(stub);
491+
492+
EchoRequest echoRequest =
493+
EchoRequest.newBuilder()
494+
.setError(Status.newBuilder().setCode(statusCode.ordinal()).build())
495+
.build();
496+
497+
Span parentSpan = tracer.spanBuilder("application-parent-operation").startSpan();
498+
try (Scope scope = parentSpan.makeCurrent()) {
499+
assertThrows(UnavailableException.class, () -> grpcClient.echo(echoRequest));
500+
} finally {
501+
parentSpan.end();
502+
}
503+
504+
List<SpanData> spans = spanExporter.getFinishedSpanItems();
505+
assertThat(spans).hasSize(attempts + 1);
506+
507+
SpanData rootSpan =
508+
spans.stream()
509+
.filter(span -> span.getName().equals("application-parent-operation"))
510+
.findFirst()
511+
.orElseThrow(() -> new AssertionError("Parent span not found"));
512+
513+
List<SpanData> attemptSpans =
514+
spans.stream()
515+
.filter(span -> span.getName().equals(SPAN_NAME_ECHO_GRPC))
516+
.collect(java.util.stream.Collectors.toList());
517+
assertThat(attemptSpans).hasSize(attempts);
518+
519+
for (SpanData attemptSpan : attemptSpans) {
520+
assertThat(attemptSpan.getKind()).isEqualTo(SpanKind.CLIENT);
521+
assertThat(attemptSpan.getParentSpanId()).isEqualTo(rootSpan.getSpanContext().getSpanId());
522+
assertThat(attemptSpan.getSpanContext().getTraceId())
523+
.isEqualTo(rootSpan.getSpanContext().getTraceId());
524+
}
525+
}
526+
459527
@Test
460528
void testTracing_retry_httpjson() throws Exception {
461529
final int attempts = 5;
@@ -534,6 +602,69 @@ void testTracing_retry_httpjson() throws Exception {
534602
assertThat(resendCounts).containsExactlyElementsIn(expectedCounts).inOrder();
535603
}
536604

605+
/**
606+
* Verifies that when an RPC is invoked within an active OpenTelemetry trace context, all attempt
607+
* spans (including the initial call and retries scheduled on background threads) properly link to
608+
* the parent span context using HTTP/JSON transport.
609+
*/
610+
@Test
611+
void testTracing_retry_httpjson_linkedToParentContext() throws Exception {
612+
final int attempts = 5;
613+
final StatusCode.Code statusCode = StatusCode.Code.INVALID_ARGUMENT;
614+
RetrySettings retrySettings =
615+
RetrySettings.newBuilder()
616+
.setTotalTimeout(org.threeten.bp.Duration.ofMillis(5000L))
617+
.setMaxAttempts(attempts)
618+
.build();
619+
620+
OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory(openTelemetrySdk);
621+
622+
EchoSettings httpJsonEchoSettings = createEchoSettings(true);
623+
EchoStubSettings.Builder httpJsonEchoSettingsBuilder =
624+
(EchoStubSettings.Builder) httpJsonEchoSettings.getStubSettings().toBuilder();
625+
httpJsonEchoSettingsBuilder
626+
.echoSettings()
627+
.setRetrySettings(retrySettings)
628+
.setRetryableCodes(statusCode);
629+
httpJsonEchoSettingsBuilder.setTracerFactory(tracingFactory);
630+
EchoStub stub = new ExtendedEchoStubSettings(httpJsonEchoSettingsBuilder).createStub();
631+
EchoClient httpClient = EchoClient.create(stub);
632+
633+
EchoRequest echoRequest =
634+
EchoRequest.newBuilder()
635+
.setError(Status.newBuilder().setCode(statusCode.ordinal()).build())
636+
.build();
637+
638+
Span parentSpan = tracer.spanBuilder("application-parent-operation").startSpan();
639+
try (Scope scope = parentSpan.makeCurrent()) {
640+
assertThrows(InvalidArgumentException.class, () -> httpClient.echo(echoRequest));
641+
} finally {
642+
parentSpan.end();
643+
}
644+
645+
List<SpanData> spans = spanExporter.getFinishedSpanItems();
646+
assertThat(spans).hasSize(attempts + 1);
647+
648+
SpanData rootSpan =
649+
spans.stream()
650+
.filter(span -> span.getName().equals("application-parent-operation"))
651+
.findFirst()
652+
.orElseThrow(() -> new AssertionError("Parent span not found"));
653+
654+
List<SpanData> attemptSpans =
655+
spans.stream()
656+
.filter(span -> span.getName().equals(SPAN_NAME_ECHO_HTTP))
657+
.collect(java.util.stream.Collectors.toList());
658+
assertThat(attemptSpans).hasSize(attempts);
659+
660+
for (SpanData attemptSpan : attemptSpans) {
661+
assertThat(attemptSpan.getKind()).isEqualTo(SpanKind.CLIENT);
662+
assertThat(attemptSpan.getParentSpanId()).isEqualTo(rootSpan.getSpanContext().getSpanId());
663+
assertThat(attemptSpan.getSpanContext().getTraceId())
664+
.isEqualTo(rootSpan.getSpanContext().getTraceId());
665+
}
666+
}
667+
537668
private SpanData getErrorSpan() {
538669
Awaitility.await()
539670
.atMost(Duration.ofSeconds(5))

0 commit comments

Comments
 (0)