@@ -52,7 +52,10 @@ class OpenTelemetryTracingTracer implements ApiTracer {
5252 private final Map <String , Object > attemptAttributes ;
5353 private final String attemptSpanName ;
5454 private final ApiTracerContext apiTracerContext ;
55- private @ Nullable Span attemptSpan ;
55+ private final io .opentelemetry .context .Context parentContext ;
56+ private final java .util .concurrent .locks .ReentrantLock lock =
57+ new java .util .concurrent .locks .ReentrantLock ();
58+ private volatile @ Nullable Span attemptSpan ;
5659
5760 @ Override
5861 public void injectTraceContext (java .util .Map <String , String > carrier ) {
@@ -82,6 +85,7 @@ public void injectTraceContext(java.util.Map<String, String> carrier) {
8285 this .apiTracerContext = apiTracerContext ;
8386 this .attemptSpanName = resolveAttemptSpanName (apiTracerContext );
8487 this .attemptAttributes = new HashMap <>();
88+ this .parentContext = io .opentelemetry .context .Context .current ();
8589 buildAttributes ();
8690 }
8791
@@ -100,6 +104,7 @@ public void injectTraceContext(java.util.Map<String, String> carrier) {
100104 this .attemptSpanName = attemptSpanName ;
101105 this .apiTracerContext = apiTracerContext ;
102106 this .attemptAttributes = new HashMap <>();
107+ this .parentContext = io .opentelemetry .context .Context .current ();
103108 buildAttributes ();
104109 }
105110
@@ -124,28 +129,59 @@ private void buildAttributes() {
124129
125130 @ Override
126131 public void attemptStarted (Object request , int attemptNumber ) {
127- Map <String , Object > currentAttemptAttributes = new HashMap <>(this .attemptAttributes );
128-
129- if (attemptNumber > 0 ) {
130- ApiTracerContext .Transport transport = apiTracerContext .transport ();
131- if (transport == ApiTracerContext .Transport .GRPC ) {
132- currentAttemptAttributes .put (
133- ObservabilityAttributes .GRPC_RESEND_COUNT_ATTRIBUTE , (long ) attemptNumber );
134- } else if (transport == ApiTracerContext .Transport .HTTP ) {
135- currentAttemptAttributes .put (
136- ObservabilityAttributes .HTTP_RESEND_COUNT_ATTRIBUTE , (long ) attemptNumber );
132+ Span oldSpan = null ;
133+ lock .lock ();
134+ try {
135+ if (attemptSpan != null ) {
136+ oldSpan = attemptSpan ;
137+ attemptSpan = null ;
138+ }
139+ Map <String , Object > currentAttemptAttributes = new HashMap <>(this .attemptAttributes );
140+
141+ if (attemptNumber > 0 ) {
142+ ApiTracerContext .Transport transport = apiTracerContext .transport ();
143+ if (transport == ApiTracerContext .Transport .GRPC ) {
144+ currentAttemptAttributes .put (
145+ ObservabilityAttributes .GRPC_RESEND_COUNT_ATTRIBUTE , (long ) attemptNumber );
146+ } else if (transport == ApiTracerContext .Transport .HTTP ) {
147+ currentAttemptAttributes .put (
148+ ObservabilityAttributes .HTTP_RESEND_COUNT_ATTRIBUTE , (long ) attemptNumber );
149+ }
137150 }
138- }
139151
140- SpanBuilder spanBuilder = tracer .spanBuilder (attemptSpanName );
152+ SpanBuilder spanBuilder = tracer .spanBuilder (attemptSpanName );
141153
142- // Attempt spans are of the CLIENT kind
143- spanBuilder .setSpanKind (SpanKind .CLIENT );
154+ // Attempt spans are of the CLIENT kind
155+ spanBuilder .setSpanKind (SpanKind .CLIENT );
144156
145- // Pass the combined attributes to the new SpanBuilder method
146- spanBuilder .setAllAttributes (ObservabilityUtils .toOtelAttributes (currentAttemptAttributes ));
157+ // Link attempt span to parent context
158+ spanBuilder .setParent (parentContext );
159+
160+ // Pass the combined attributes to the new SpanBuilder method
161+ spanBuilder .setAllAttributes (ObservabilityUtils .toOtelAttributes (currentAttemptAttributes ));
162+
163+ this .attemptSpan = spanBuilder .startSpan ();
164+ } finally {
165+ lock .unlock ();
166+ }
167+ if (oldSpan != null ) {
168+ endAttemptSpan (oldSpan , null );
169+ }
170+ }
171+
172+ @ Override
173+ public void operationSucceeded () {
174+ recordErrorAndEndAttempt (null );
175+ }
176+
177+ @ Override
178+ public void operationCancelled () {
179+ recordErrorAndEndAttempt (new CancellationException ());
180+ }
147181
148- this .attemptSpan = spanBuilder .startSpan ();
182+ @ Override
183+ public void operationFailed (Throwable error ) {
184+ recordErrorAndEndAttempt (error );
149185 }
150186
151187 @ Override
@@ -155,12 +191,13 @@ public void attemptSucceeded() {
155191
156192 @ Override
157193 public void responseHeadersReceived (java .util .Map <String , Object > headers ) {
158- if (attemptSpan == null ) {
194+ Span currentSpan = attemptSpan ;
195+ if (currentSpan == null ) {
159196 return ;
160197 }
161198 long contentLength = extractContentLength (headers );
162199 if (contentLength >= 0 ) {
163- attemptSpan .setAttribute (ObservabilityAttributes .HTTP_RESPONSE_BODY_SIZE , contentLength );
200+ currentSpan .setAttribute (ObservabilityAttributes .HTTP_RESPONSE_BODY_SIZE , contentLength );
164201 }
165202 }
166203
@@ -216,41 +253,46 @@ public void attemptPermanentFailure(Throwable error) {
216253 }
217254
218255 private void recordErrorAndEndAttempt (@ Nullable Throwable error ) {
219- if (attemptSpan == null ) {
220- return ;
256+ Span localAttemptSpan ;
257+ lock .lock ();
258+ try {
259+ localAttemptSpan = attemptSpan ;
260+ if (localAttemptSpan == null ) {
261+ return ;
262+ }
263+ attemptSpan = null ;
264+ } finally {
265+ lock .unlock ();
221266 }
267+
268+ endAttemptSpan (localAttemptSpan , error );
269+ }
270+
271+ private void endAttemptSpan (Span localAttemptSpan , @ Nullable Throwable error ) {
222272 Map <String , Object > responseAttributes =
223273 ObservabilityUtils .getResponseAttributes (error , this .apiTracerContext .transport ());
224274 if (!responseAttributes .isEmpty ()) {
225- attemptSpan .setAllAttributes (ObservabilityUtils .toOtelAttributes (responseAttributes ));
275+ localAttemptSpan .setAllAttributes (ObservabilityUtils .toOtelAttributes (responseAttributes ));
226276 }
227277
228278 if (error != null && !Strings .isNullOrEmpty (error .getMessage ())) {
229- attemptSpan .setAttribute (
279+ localAttemptSpan .setAttribute (
230280 ObservabilityAttributes .STATUS_MESSAGE_ATTRIBUTE , error .getMessage ());
231281 }
232282
233- endAttempt ();
234- }
235-
236- private void endAttempt () {
237- if (attemptSpan == null ) {
238- return ;
239- }
240-
241- attemptSpan .end ();
242- attemptSpan = null ;
283+ localAttemptSpan .end ();
243284 }
244285
245286 @ Override
246287 public void requestUrlResolved (String url ) {
247- if (attemptSpan == null ) {
288+ Span currentSpan = attemptSpan ;
289+ if (currentSpan == null ) {
248290 return ;
249291 }
250292 String sanitizedUrlString = ObservabilityUtils .sanitizeUrlFull (url );
251293 if (sanitizedUrlString .isEmpty ()) {
252294 return ;
253295 }
254- attemptSpan .setAttribute (ObservabilityAttributes .HTTP_URL_FULL_ATTRIBUTE , sanitizedUrlString );
296+ currentSpan .setAttribute (ObservabilityAttributes .HTTP_URL_FULL_ATTRIBUTE , sanitizedUrlString );
255297 }
256298}
0 commit comments