Skip to content

Commit d6587f4

Browse files
authored
fix: update otel integration to properly activate span context for lazy RPCs such as reads & writes (googleapis#3255)
* Fixes for WriteChannel * Fixes for RadChannel * Fixes for BlobWriteSession * Fixes for BlobReadSession `s$com.google.cloud.storage.Storage/readAs$com.google.cloud.storage.Storage/blobReadSession/readAs$g` * Fixes for BlobAppendableUpload
1 parent 7bd73d3 commit d6587f4

File tree

1 file changed

+91
-10
lines changed

1 file changed

+91
-10
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/OtelStorageDecorator.java

Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@
6464
import org.checkerframework.checker.nullness.qual.Nullable;
6565

6666
@SuppressWarnings("DuplicatedCode")
67-
final class OtelStorageDecorator implements Storage {
67+
public final class OtelStorageDecorator implements Storage {
6868

6969
/** Becomes the {@code otel.scope.name} attribute in a span */
7070
private static final String OTEL_SCOPE_NAME = "cloud.google.com/java/storage";
7171

72+
private static final String BLOB_READ_SESSION = "blobReadSession";
73+
7274
@VisibleForTesting final Storage delegate;
7375
private final OpenTelemetry otel;
7476
private final Attributes baseAttributes;
@@ -1434,13 +1436,11 @@ public BlobWriteSession blobWriteSession(BlobInfo blobInfo, BlobWriteOption... o
14341436
.startSpan();
14351437
try (Scope ignore = sessionSpan.makeCurrent()) {
14361438
BlobWriteSession session = delegate.blobWriteSession(blobInfo, options);
1437-
return new OtelDecoratedBlobWriteSession(session);
1439+
return new OtelDecoratedBlobWriteSession(session, sessionSpan);
14381440
} catch (Throwable t) {
14391441
sessionSpan.recordException(t);
14401442
sessionSpan.setStatus(StatusCode.ERROR, t.getClass().getSimpleName());
14411443
throw t;
1442-
} finally {
1443-
sessionSpan.end();
14441444
}
14451445
}
14461446

@@ -1467,12 +1467,12 @@ public Blob moveBlob(MoveBlobRequest request) {
14671467
public ApiFuture<BlobReadSession> blobReadSession(BlobId id, BlobSourceOption... options) {
14681468
Span blobReadSessionSpan =
14691469
tracer
1470-
.spanBuilder("blobReadSession")
1470+
.spanBuilder(BLOB_READ_SESSION)
14711471
.setAttribute("gsutil.uri", id.toGsUtilUriWithGeneration())
14721472
.startSpan();
14731473
try (Scope ignore1 = blobReadSessionSpan.makeCurrent()) {
14741474
Context blobReadSessionContext = Context.current();
1475-
Span ready = tracer.spanBuilder("blobReadSession/ready").startSpan();
1475+
Span ready = tracer.spanBuilder(BLOB_READ_SESSION + "/ready").startSpan();
14761476
ApiFuture<BlobReadSession> blobReadSessionApiFuture = delegate.blobReadSession(id, options);
14771477
ApiFuture<BlobReadSession> futureDecorated =
14781478
ApiFutures.transform(
@@ -1561,7 +1561,7 @@ static UnaryOperator<RetryContext> retryContextDecorator(OpenTelemetry otel) {
15611561
return String.format(Locale.US, "gs://%s/", bucket);
15621562
}
15631563

1564-
private static final class TracerDecorator implements Tracer {
1564+
public static final class TracerDecorator implements Tracer {
15651565
@Nullable private final Context parentContextOverride;
15661566
private final Tracer delegate;
15671567
private final Attributes baseAttributes;
@@ -1578,7 +1578,7 @@ private TracerDecorator(
15781578
this.spanNamePrefix = spanNamePrefix;
15791579
}
15801580

1581-
private static TracerDecorator decorate(
1581+
public static TracerDecorator decorate(
15821582
@Nullable Context parentContextOverride,
15831583
OpenTelemetry otel,
15841584
Attributes baseAttributes,
@@ -1608,13 +1608,16 @@ static final class OtelDecoratedReadChannel implements ReadChannel {
16081608
@VisibleForTesting final ReadChannel reader;
16091609
private final Span span;
16101610

1611+
private volatile Scope scope;
1612+
16111613
private OtelDecoratedReadChannel(ReadChannel reader, Span span) {
16121614
this.reader = reader;
16131615
this.span = span;
16141616
}
16151617

16161618
@Override
16171619
public void seek(long position) throws IOException {
1620+
clearScope();
16181621
reader.seek(position);
16191622
}
16201623

@@ -1630,6 +1633,7 @@ public RestorableState<ReadChannel> capture() {
16301633

16311634
@Override
16321635
public ReadChannel limit(long limit) {
1636+
clearScope();
16331637
return reader.limit(limit);
16341638
}
16351639

@@ -1640,6 +1644,7 @@ public long limit() {
16401644

16411645
@Override
16421646
public int read(ByteBuffer dst) throws IOException {
1647+
setScope();
16431648
return reader.read(dst);
16441649
}
16451650

@@ -1650,21 +1655,38 @@ public boolean isOpen() {
16501655

16511656
@Override
16521657
public void close() {
1658+
setScope();
16531659
try {
16541660
reader.close();
16551661
} finally {
16561662
span.end();
1663+
clearScope();
1664+
}
1665+
}
1666+
1667+
private void clearScope() {
1668+
try (Scope ignore = scope) {
1669+
scope = null;
1670+
}
1671+
}
1672+
1673+
public void setScope() {
1674+
if (scope != null) {
1675+
clearScope();
16571676
}
1677+
scope = span.makeCurrent();
16581678
}
16591679
}
16601680

16611681
private final class OtelDecoratedBlobWriteSession implements BlobWriteSession {
16621682

16631683
private final BlobWriteSession delegate;
1684+
private final Span sessionSpan;
16641685
private final Tracer tracer;
16651686

1666-
public OtelDecoratedBlobWriteSession(BlobWriteSession delegate) {
1687+
public OtelDecoratedBlobWriteSession(BlobWriteSession delegate, Span sessionSpan) {
16671688
this.delegate = delegate;
1689+
this.sessionSpan = sessionSpan;
16681690
this.tracer =
16691691
TracerDecorator.decorate(
16701692
Context.current(),
@@ -1696,13 +1718,16 @@ private class OtelDecoratingWritableByteChannel implements WritableByteChannel {
16961718
private final WritableByteChannel delegate;
16971719
private final Span openSpan;
16981720

1721+
private Scope scope;
1722+
16991723
private OtelDecoratingWritableByteChannel(WritableByteChannel delegate, Span openSpan) {
17001724
this.delegate = delegate;
17011725
this.openSpan = openSpan;
17021726
}
17031727

17041728
@Override
17051729
public int write(ByteBuffer src) throws IOException {
1730+
setScope();
17061731
return delegate.write(src);
17071732
}
17081733

@@ -1713,16 +1738,34 @@ public boolean isOpen() {
17131738

17141739
@Override
17151740
public void close() throws IOException {
1741+
setScope();
17161742
try {
17171743
delegate.close();
17181744
} catch (IOException | RuntimeException e) {
17191745
openSpan.recordException(e);
17201746
openSpan.setStatus(StatusCode.ERROR, e.getClass().getSimpleName());
1747+
sessionSpan.recordException(e);
1748+
sessionSpan.setStatus(StatusCode.ERROR, e.getClass().getSimpleName());
17211749
throw e;
17221750
} finally {
17231751
openSpan.end();
1752+
sessionSpan.end();
1753+
clearScope();
1754+
}
1755+
}
1756+
1757+
private void clearScope() {
1758+
try (Scope ignore = scope) {
1759+
scope = null;
17241760
}
17251761
}
1762+
1763+
public void setScope() {
1764+
if (scope != null) {
1765+
clearScope();
1766+
}
1767+
scope = openSpan.makeCurrent();
1768+
}
17261769
}
17271770
}
17281771

@@ -1731,6 +1774,8 @@ static final class OtelDecoratedWriteChannel implements WriteChannel {
17311774
@VisibleForTesting final WriteChannel delegate;
17321775
private final Span openSpan;
17331776

1777+
private Scope scope;
1778+
17341779
private OtelDecoratedWriteChannel(WriteChannel delegate, Span openSpan) {
17351780
this.delegate = delegate;
17361781
this.openSpan = openSpan;
@@ -1748,6 +1793,7 @@ public RestorableState<WriteChannel> capture() {
17481793

17491794
@Override
17501795
public int write(ByteBuffer src) throws IOException {
1796+
setScope();
17511797
return delegate.write(src);
17521798
}
17531799

@@ -1758,6 +1804,7 @@ public boolean isOpen() {
17581804

17591805
@Override
17601806
public void close() throws IOException {
1807+
setScope();
17611808
try {
17621809
delegate.close();
17631810
} catch (IOException | RuntimeException e) {
@@ -1766,7 +1813,21 @@ public void close() throws IOException {
17661813
throw e;
17671814
} finally {
17681815
openSpan.end();
1816+
clearScope();
1817+
}
1818+
}
1819+
1820+
private void clearScope() {
1821+
try (Scope ignore = scope) {
1822+
scope = null;
1823+
}
1824+
}
1825+
1826+
public void setScope() {
1827+
if (scope != null) {
1828+
clearScope();
17691829
}
1830+
scope = openSpan.makeCurrent();
17701831
}
17711832
}
17721833

@@ -1962,7 +2023,7 @@ public BlobInfo getBlobInfo() {
19622023
public <Projection> Projection readAs(ReadProjectionConfig<Projection> config) {
19632024
Span readRangeSpan =
19642025
tracer
1965-
.spanBuilder("readAs")
2026+
.spanBuilder(BLOB_READ_SESSION + "/readAs")
19662027
.setAttribute("gsutil.uri", id.toGsUtilUriWithGeneration())
19672028
.setParent(blobReadSessionContext)
19682029
.startSpan();
@@ -2145,6 +2206,8 @@ private final class OtelDecoratingAppendableUploadWriteableByteChannel
21452206
private final AppendableUploadWriteableByteChannel delegate;
21462207
private final Span openSpan;
21472208

2209+
private volatile Scope scope;
2210+
21482211
private OtelDecoratingAppendableUploadWriteableByteChannel(
21492212
AppendableUploadWriteableByteChannel delegate, Span openSpan) {
21502213
this.delegate = delegate;
@@ -2165,6 +2228,7 @@ public void finalizeAndClose() throws IOException {
21652228
} finally {
21662229
openSpan.end();
21672230
uploadSpan.end();
2231+
clearScope();
21682232
}
21692233
}
21702234

@@ -2182,12 +2246,14 @@ public void closeWithoutFinalizing() throws IOException {
21822246
} finally {
21832247
openSpan.end();
21842248
uploadSpan.end();
2249+
clearScope();
21852250
}
21862251
}
21872252

21882253
@Override
21892254
@BetaApi
21902255
public void close() throws IOException {
2256+
setScope();
21912257
try {
21922258
delegate.close();
21932259
} catch (IOException | RuntimeException e) {
@@ -2199,18 +2265,33 @@ public void close() throws IOException {
21992265
} finally {
22002266
openSpan.end();
22012267
uploadSpan.end();
2268+
clearScope();
22022269
}
22032270
}
22042271

22052272
@Override
22062273
public int write(ByteBuffer src) throws IOException {
2274+
setScope();
22072275
return delegate.write(src);
22082276
}
22092277

22102278
@Override
22112279
public boolean isOpen() {
22122280
return delegate.isOpen();
22132281
}
2282+
2283+
private void clearScope() {
2284+
try (Scope ignore = scope) {
2285+
scope = null;
2286+
}
2287+
}
2288+
2289+
public void setScope() {
2290+
if (scope != null) {
2291+
clearScope();
2292+
}
2293+
scope = openSpan.makeCurrent();
2294+
}
22142295
}
22152296
}
22162297
}

0 commit comments

Comments
 (0)