6868import io .grpc .MethodDescriptor ;
6969import io .opentelemetry .api .GlobalOpenTelemetry ;
7070import io .opentelemetry .api .common .AttributeKey ;
71+ import io .opentelemetry .api .trace .Span ;
7172import io .opentelemetry .api .trace .SpanKind ;
73+ import io .opentelemetry .api .trace .Tracer ;
74+ import io .opentelemetry .context .Scope ;
7275import io .opentelemetry .sdk .OpenTelemetrySdk ;
7376import io .opentelemetry .sdk .testing .exporter .InMemorySpanExporter ;
7477import io .opentelemetry .sdk .trace .SdkTracerProvider ;
@@ -127,6 +130,7 @@ class ITOtelTracing {
127130
128131 private InMemorySpanExporter spanExporter ;
129132 private OpenTelemetrySdk openTelemetrySdk ;
133+ private Tracer tracer ;
130134
131135 @ BeforeEach
132136 void setup () {
@@ -139,6 +143,7 @@ void setup() {
139143
140144 openTelemetrySdk =
141145 OpenTelemetrySdk .builder ().setTracerProvider (tracerProvider ).buildAndRegisterGlobal ();
146+ tracer = openTelemetrySdk .getTracer ("it-otel-tracing-test" );
142147 }
143148
144149 @ AfterEach
@@ -218,6 +223,42 @@ void testTracing_successfulEcho_grpc() throws Exception {
218223 }
219224 }
220225
226+ @ Test
227+ void testTracing_successfulEcho_grpc_linkedToParentContext () throws Exception {
228+ OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory (openTelemetrySdk );
229+
230+ EchoSettings grpcEchoSettings = createEchoSettings (false );
231+ EchoStub stub = createStubWithServiceName (grpcEchoSettings , tracingFactory );
232+
233+ try (EchoClient client = EchoClient .create (stub )) {
234+ Span parentSpan = tracer .spanBuilder ("application-parent-operation" ).startSpan ();
235+ try (Scope scope = parentSpan .makeCurrent ()) {
236+ client .echo (EchoRequest .newBuilder ().setContent ("tracing-test" ).build ());
237+ } finally {
238+ parentSpan .end ();
239+ }
240+
241+ List <SpanData > spans = spanExporter .getFinishedSpanItems ();
242+ assertThat (spans ).isNotEmpty ();
243+
244+ SpanData attemptSpan =
245+ spans .stream ()
246+ .filter (span -> span .getName ().equals (SPAN_NAME_ECHO_GRPC ))
247+ .findFirst ()
248+ .orElseThrow (() -> new AssertionError ("Attempt span not found" ));
249+ SpanData rootSpan =
250+ spans .stream ()
251+ .filter (span -> span .getName ().equals ("application-parent-operation" ))
252+ .findFirst ()
253+ .orElseThrow (() -> new AssertionError ("Parent span not found" ));
254+
255+ assertThat (attemptSpan .getKind ()).isEqualTo (SpanKind .CLIENT );
256+ assertThat (attemptSpan .getParentSpanId ()).isEqualTo (rootSpan .getSpanContext ().getSpanId ());
257+ assertThat (attemptSpan .getSpanContext ().getTraceId ())
258+ .isEqualTo (rootSpan .getSpanContext ().getTraceId ());
259+ }
260+ }
261+
221262 @ Test
222263 void testTracing_successfulEcho_httpjson () throws Exception {
223264 OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory (openTelemetrySdk );
@@ -305,6 +346,42 @@ void testTracing_successfulEcho_httpjson() throws Exception {
305346 }
306347 }
307348
349+ @ Test
350+ void testTracing_successfulEcho_httpjson_linkedToParentContext () throws Exception {
351+ OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory (openTelemetrySdk );
352+
353+ EchoSettings httpJsonEchoSettings = createEchoSettings (true );
354+ EchoStub stub = createStubWithServiceName (httpJsonEchoSettings , tracingFactory );
355+
356+ try (EchoClient client = EchoClient .create (stub )) {
357+ Span parentSpan = tracer .spanBuilder ("application-parent-operation" ).startSpan ();
358+ try (Scope scope = parentSpan .makeCurrent ()) {
359+ client .echo (EchoRequest .newBuilder ().setContent ("tracing-test" ).build ());
360+ } finally {
361+ parentSpan .end ();
362+ }
363+
364+ List <SpanData > spans = spanExporter .getFinishedSpanItems ();
365+ assertThat (spans ).isNotEmpty ();
366+
367+ SpanData attemptSpan =
368+ spans .stream ()
369+ .filter (span -> span .getName ().equals (SPAN_NAME_ECHO_HTTP ))
370+ .findFirst ()
371+ .orElseThrow (() -> new AssertionError ("Attempt span not found" ));
372+ SpanData rootSpan =
373+ spans .stream ()
374+ .filter (span -> span .getName ().equals ("application-parent-operation" ))
375+ .findFirst ()
376+ .orElseThrow (() -> new AssertionError ("Parent span not found" ));
377+
378+ assertThat (attemptSpan .getKind ()).isEqualTo (SpanKind .CLIENT );
379+ assertThat (attemptSpan .getParentSpanId ()).isEqualTo (rootSpan .getSpanContext ().getSpanId ());
380+ assertThat (attemptSpan .getSpanContext ().getTraceId ())
381+ .isEqualTo (rootSpan .getSpanContext ().getTraceId ());
382+ }
383+ }
384+
308385 private long computeExpectedHttpJsonResponseSize (Message message )
309386 throws InvalidProtocolBufferException {
310387 String jsonPayload = com .google .protobuf .util .JsonFormat .printer ().print (message );
@@ -441,6 +518,64 @@ void testTracing_retry_grpc() throws Exception {
441518 assertThat (resendCounts ).containsExactlyElementsIn (expectedCounts ).inOrder ();
442519 }
443520
521+ @ Test
522+ void testTracing_retry_grpc_linkedToParentContext () throws Exception {
523+ final int attempts = 5 ;
524+ final StatusCode .Code statusCode = StatusCode .Code .UNAVAILABLE ;
525+ RetrySettings retrySettings =
526+ RetrySettings .newBuilder ()
527+ .setTotalTimeout (org .threeten .bp .Duration .ofMillis (5000L ))
528+ .setMaxAttempts (attempts )
529+ .build ();
530+
531+ OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory (openTelemetrySdk );
532+
533+ EchoSettings grpcEchoSettings = createEchoSettings (false );
534+ EchoStubSettings .Builder grpcEchoSettingsBuilder =
535+ (EchoStubSettings .Builder ) grpcEchoSettings .getStubSettings ().toBuilder ();
536+ grpcEchoSettingsBuilder
537+ .echoSettings ()
538+ .setRetrySettings (retrySettings )
539+ .setRetryableCodes (statusCode );
540+ grpcEchoSettingsBuilder .setTracerFactory (tracingFactory );
541+ EchoStub stub = new ExtendedEchoStubSettings (grpcEchoSettingsBuilder ).createStub ();
542+ EchoClient grpcClient = EchoClient .create (stub );
543+
544+ EchoRequest echoRequest =
545+ EchoRequest .newBuilder ()
546+ .setError (Status .newBuilder ().setCode (statusCode .ordinal ()).build ())
547+ .build ();
548+
549+ Span parentSpan = tracer .spanBuilder ("application-parent-operation" ).startSpan ();
550+ try (Scope scope = parentSpan .makeCurrent ()) {
551+ assertThrows (UnavailableException .class , () -> grpcClient .echo (echoRequest ));
552+ } finally {
553+ parentSpan .end ();
554+ }
555+
556+ List <SpanData > spans = spanExporter .getFinishedSpanItems ();
557+ assertThat (spans ).hasSize (attempts + 1 );
558+
559+ SpanData rootSpan =
560+ spans .stream ()
561+ .filter (span -> span .getName ().equals ("application-parent-operation" ))
562+ .findFirst ()
563+ .orElseThrow (() -> new AssertionError ("Parent span not found" ));
564+
565+ List <SpanData > attemptSpans =
566+ spans .stream ()
567+ .filter (span -> span .getName ().equals (SPAN_NAME_ECHO_GRPC ))
568+ .collect (java .util .stream .Collectors .toList ());
569+ assertThat (attemptSpans ).hasSize (attempts );
570+
571+ for (SpanData attemptSpan : attemptSpans ) {
572+ assertThat (attemptSpan .getKind ()).isEqualTo (SpanKind .CLIENT );
573+ assertThat (attemptSpan .getParentSpanId ()).isEqualTo (rootSpan .getSpanContext ().getSpanId ());
574+ assertThat (attemptSpan .getSpanContext ().getTraceId ())
575+ .isEqualTo (rootSpan .getSpanContext ().getTraceId ());
576+ }
577+ }
578+
444579 @ Test
445580 void testTracing_retry_httpjson () throws Exception {
446581 final int attempts = 5 ;
@@ -519,6 +654,64 @@ void testTracing_retry_httpjson() throws Exception {
519654 assertThat (resendCounts ).containsExactlyElementsIn (expectedCounts ).inOrder ();
520655 }
521656
657+ @ Test
658+ void testTracing_retry_httpjson_linkedToParentContext () throws Exception {
659+ final int attempts = 5 ;
660+ final StatusCode .Code statusCode = StatusCode .Code .INVALID_ARGUMENT ;
661+ RetrySettings retrySettings =
662+ RetrySettings .newBuilder ()
663+ .setTotalTimeout (org .threeten .bp .Duration .ofMillis (5000L ))
664+ .setMaxAttempts (attempts )
665+ .build ();
666+
667+ OpenTelemetryTracingFactory tracingFactory = new OpenTelemetryTracingFactory (openTelemetrySdk );
668+
669+ EchoSettings httpJsonEchoSettings = createEchoSettings (true );
670+ EchoStubSettings .Builder httpJsonEchoSettingsBuilder =
671+ (EchoStubSettings .Builder ) httpJsonEchoSettings .getStubSettings ().toBuilder ();
672+ httpJsonEchoSettingsBuilder
673+ .echoSettings ()
674+ .setRetrySettings (retrySettings )
675+ .setRetryableCodes (statusCode );
676+ httpJsonEchoSettingsBuilder .setTracerFactory (tracingFactory );
677+ EchoStub stub = new ExtendedEchoStubSettings (httpJsonEchoSettingsBuilder ).createStub ();
678+ EchoClient httpClient = EchoClient .create (stub );
679+
680+ EchoRequest echoRequest =
681+ EchoRequest .newBuilder ()
682+ .setError (Status .newBuilder ().setCode (statusCode .ordinal ()).build ())
683+ .build ();
684+
685+ Span parentSpan = tracer .spanBuilder ("application-parent-operation" ).startSpan ();
686+ try (Scope scope = parentSpan .makeCurrent ()) {
687+ assertThrows (InvalidArgumentException .class , () -> httpClient .echo (echoRequest ));
688+ } finally {
689+ parentSpan .end ();
690+ }
691+
692+ List <SpanData > spans = spanExporter .getFinishedSpanItems ();
693+ assertThat (spans ).hasSize (attempts + 1 );
694+
695+ SpanData rootSpan =
696+ spans .stream ()
697+ .filter (span -> span .getName ().equals ("application-parent-operation" ))
698+ .findFirst ()
699+ .orElseThrow (() -> new AssertionError ("Parent span not found" ));
700+
701+ List <SpanData > attemptSpans =
702+ spans .stream ()
703+ .filter (span -> span .getName ().equals (SPAN_NAME_ECHO_HTTP ))
704+ .collect (java .util .stream .Collectors .toList ());
705+ assertThat (attemptSpans ).hasSize (attempts );
706+
707+ for (SpanData attemptSpan : attemptSpans ) {
708+ assertThat (attemptSpan .getKind ()).isEqualTo (SpanKind .CLIENT );
709+ assertThat (attemptSpan .getParentSpanId ()).isEqualTo (rootSpan .getSpanContext ().getSpanId ());
710+ assertThat (attemptSpan .getSpanContext ().getTraceId ())
711+ .isEqualTo (rootSpan .getSpanContext ().getTraceId ());
712+ }
713+ }
714+
522715 private SpanData getErrorSpan () {
523716 List <SpanData > spans = spanExporter .getFinishedSpanItems ();
524717 assertThat (spans ).isNotEmpty ();
0 commit comments