Skip to content

Commit 030aa62

Browse files
committed
fix(gax): manage OpenTelemetry scope lifecycle internally in OpenTelemetryTracingTracer
1 parent 3b44757 commit 030aa62

2 files changed

Lines changed: 90 additions & 20 deletions

File tree

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingTracer.java

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class OpenTelemetryTracingTracer implements ApiTracer {
5353
private final String attemptSpanName;
5454
private final ApiTracerContext apiTracerContext;
5555
private @Nullable Span attemptSpan;
56+
private io.opentelemetry.context.@Nullable Scope attemptScope;
5657

5758
@Override
5859
public void injectTraceContext(java.util.Map<String, String> carrier) {
@@ -71,18 +72,6 @@ public void injectTraceContext(java.util.Map<String, String> carrier) {
7172
}
7273
}
7374

74-
@Override
75-
public Scope inScope() {
76-
if (attemptSpan == null) {
77-
return () -> {};
78-
}
79-
// Suppressing to make MustBeClosedChecker happy. The scope will be closed indirectly
80-
// via the returned ApiTracer.Scope, and the checker cannot recognize it.
81-
@SuppressWarnings("MustBeClosedChecker")
82-
io.opentelemetry.context.Scope otelScope = attemptSpan.makeCurrent();
83-
return otelScope::close;
84-
}
85-
8675
/**
8776
* Creates a new instance of {@code OpenTelemetryTracingTracer}.
8877
*
@@ -136,6 +125,24 @@ private void buildAttributes() {
136125

137126
@Override
138127
public void attemptStarted(Object request, int attemptNumber) {
128+
Span oldSpan = null;
129+
io.opentelemetry.context.Scope oldScope = null;
130+
if (attemptSpan != null) {
131+
oldSpan = attemptSpan;
132+
attemptSpan = null;
133+
oldScope = attemptScope;
134+
attemptScope = null;
135+
}
136+
if (oldScope != null) {
137+
try {
138+
oldScope.close();
139+
} catch (Exception ignored) {
140+
}
141+
}
142+
if (oldSpan != null) {
143+
oldSpan.end();
144+
}
145+
139146
Map<String, Object> currentAttemptAttributes = new HashMap<>(this.attemptAttributes);
140147

141148
if (attemptNumber > 0) {
@@ -158,6 +165,10 @@ public void attemptStarted(Object request, int attemptNumber) {
158165
spanBuilder.setAllAttributes(ObservabilityUtils.toOtelAttributes(currentAttemptAttributes));
159166

160167
this.attemptSpan = spanBuilder.startSpan();
168+
// Suppressing to make MustBeClosedChecker happy. The scope will be closed in endAttempt().
169+
@SuppressWarnings("MustBeClosedChecker")
170+
io.opentelemetry.context.Scope localScope = this.attemptSpan.makeCurrent();
171+
this.attemptScope = localScope;
161172
}
162173

163174
@Override
@@ -250,10 +261,34 @@ private void endAttempt() {
250261
return;
251262
}
252263

264+
if (attemptScope != null) {
265+
try {
266+
attemptScope.close();
267+
} catch (Exception ignored) {
268+
} finally {
269+
attemptScope = null;
270+
}
271+
}
272+
253273
attemptSpan.end();
254274
attemptSpan = null;
255275
}
256276

277+
@Override
278+
public void operationSucceeded() {
279+
recordErrorAndEndAttempt(null);
280+
}
281+
282+
@Override
283+
public void operationCancelled() {
284+
recordErrorAndEndAttempt(new CancellationException());
285+
}
286+
287+
@Override
288+
public void operationFailed(Throwable error) {
289+
recordErrorAndEndAttempt(error);
290+
}
291+
257292
@Override
258293
public void requestUrlResolved(String url) {
259294
if (attemptSpan == null) {

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/tracing/OpenTelemetryTracingTracerTest.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -687,21 +687,56 @@ void testInjectTraceContext_addsHeaders() {
687687
}
688688

689689
@Test
690-
void testInScope_whenAttemptSpanActive_makesSpanCurrentAndClosesScope() {
690+
void testAttemptStarted_makesSpanCurrent() {
691691
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
692-
ApiTracer.Scope tracerScope = openTelemetryTracingTracer.inScope();
693692
verify(span).makeCurrent();
693+
}
694+
695+
@Test
696+
void testAttemptEnded_closesScope_succeeded() {
697+
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
698+
openTelemetryTracingTracer.attemptSucceeded();
699+
verify(scope).close();
700+
verify(span).end();
701+
}
694702

695-
tracerScope.close();
703+
@Test
704+
void testAttemptEnded_closesScope_failed() {
705+
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
706+
openTelemetryTracingTracer.attemptFailedRetriesExhausted(new RuntimeException());
696707
verify(scope).close();
708+
verify(span).end();
697709
}
698710

699711
@Test
700-
void testInScope_whenAttemptSpanNull_returnsNoopScope() {
701-
ApiTracer.Scope tracerScope = openTelemetryTracingTracer.inScope();
702-
verify(span, never()).makeCurrent();
712+
void testAttemptEnded_closesScope_cancelled() {
713+
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
714+
openTelemetryTracingTracer.attemptCancelled();
715+
verify(scope).close();
716+
verify(span).end();
717+
}
703718

704-
tracerScope.close();
705-
verify(scope, never()).close();
719+
@Test
720+
void testOperationSucceeded_closesScope() {
721+
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
722+
openTelemetryTracingTracer.operationSucceeded();
723+
verify(scope).close();
724+
verify(span).end();
725+
}
726+
727+
@Test
728+
void testOperationCancelled_closesScope() {
729+
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
730+
openTelemetryTracingTracer.operationCancelled();
731+
verify(scope).close();
732+
verify(span).end();
733+
}
734+
735+
@Test
736+
void testOperationFailed_closesScope() {
737+
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
738+
openTelemetryTracingTracer.operationFailed(new RuntimeException());
739+
verify(scope).close();
740+
verify(span).end();
706741
}
707742
}

0 commit comments

Comments
 (0)