diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java index e244903d09..bb69d83000 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java @@ -848,9 +848,10 @@ private Get createReadRequest(StorageObject from, Map options) throws @Override public long read( StorageObject from, Map options, long position, OutputStream outputStream) { + OpenTelemetryTraceUtil.Span otelSpan = openTelemetryTraceUtil.startSpan("read"); Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_READ); Scope scope = tracer.withSpan(span); - try { + try (OpenTelemetryTraceUtil.Scope unused = otelSpan.makeCurrent()) { Get req = createReadRequest(from, options); Boolean shouldReturnRawInputStream = Option.RETURN_RAW_INPUT_STREAM.getBoolean(options); if (shouldReturnRawInputStream != null) { @@ -867,6 +868,8 @@ public long read( req.executeMedia().download(outputStream); return mediaHttpDownloader.getNumBytesDownloaded(); } catch (IOException ex) { + otelSpan.recordException(ex); + otelSpan.setStatus(StatusCode.ERROR, ex.getClass().getSimpleName()); span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); StorageException serviceException = translate(ex); if (serviceException.getCode() == SC_REQUESTED_RANGE_NOT_SATISFIABLE) { @@ -874,6 +877,7 @@ public long read( } throw serviceException; } finally { + otelSpan.end(); scope.close(); span.end(HttpStorageRpcSpans.END_SPAN_OPTIONS); } @@ -882,9 +886,10 @@ public long read( @Override public Tuple read( StorageObject from, Map options, long position, int bytes) { + OpenTelemetryTraceUtil.Span otelSpan = openTelemetryTraceUtil.startSpan("read"); Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_READ); Scope scope = tracer.withSpan(span); - try { + try (OpenTelemetryTraceUtil.Scope unused = otelSpan.makeCurrent()) { checkArgument(position >= 0, "Position should be non-negative, is " + position); Get req = createReadRequest(from, options); Boolean shouldReturnRawInputStream = Option.RETURN_RAW_INPUT_STREAM.getBoolean(options); @@ -902,6 +907,8 @@ public Tuple read( String etag = req.getLastResponseHeaders().getETag(); return Tuple.of(etag, output.toByteArray()); } catch (IOException ex) { + otelSpan.recordException(ex); + otelSpan.setStatus(StatusCode.ERROR, ex.getClass().getSimpleName()); span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); StorageException serviceException = StorageException.translate(ex); if (serviceException.getCode() == SC_REQUESTED_RANGE_NOT_SATISFIABLE) { @@ -909,6 +916,7 @@ public Tuple read( } throw serviceException; } finally { + otelSpan.end(); scope.close(); span.end(HttpStorageRpcSpans.END_SPAN_OPTIONS); } @@ -1158,11 +1166,13 @@ public String open(String signedURL) { @Override public RewriteResponse openRewrite(RewriteRequest rewriteRequest) { + OpenTelemetryTraceUtil.Span otelSpan = openTelemetryTraceUtil.startSpan("openRewrite"); Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_OPEN_REWRITE); Scope scope = tracer.withSpan(span); - try { - return rewrite(rewriteRequest, null); + try (OpenTelemetryTraceUtil.Scope unused = otelSpan.makeCurrent()) { + return rewrite(rewriteRequest, null, openTelemetryTraceUtil.currentContext()); } finally { + otelSpan.end(); scope.close(); span.end(HttpStorageRpcSpans.END_SPAN_OPTIONS); } @@ -1170,18 +1180,25 @@ public RewriteResponse openRewrite(RewriteRequest rewriteRequest) { @Override public RewriteResponse continueRewrite(RewriteResponse previousResponse) { + OpenTelemetryTraceUtil.Span otelSpan = openTelemetryTraceUtil.startSpan("continueRewrite"); Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_CONTINUE_REWRITE); Scope scope = tracer.withSpan(span); - try { - return rewrite(previousResponse.rewriteRequest, previousResponse.rewriteToken); + try (OpenTelemetryTraceUtil.Scope unused = otelSpan.makeCurrent()) { + return rewrite( + previousResponse.rewriteRequest, + previousResponse.rewriteToken, + openTelemetryTraceUtil.currentContext()); } finally { + otelSpan.end(); scope.close(); span.end(HttpStorageRpcSpans.END_SPAN_OPTIONS); } } - private RewriteResponse rewrite(RewriteRequest req, String token) { - try { + private RewriteResponse rewrite( + RewriteRequest req, String token, OpenTelemetryTraceUtil.Context ctx) { + OpenTelemetryTraceUtil.Span otelSpan = openTelemetryTraceUtil.startSpan("rewrite", ctx); + try (OpenTelemetryTraceUtil.Scope unused = otelSpan.makeCurrent()) { String userProject = Option.USER_PROJECT.getString(req.sourceOptions); if (userProject == null) { userProject = Option.USER_PROJECT.getString(req.targetOptions); @@ -1232,8 +1249,12 @@ private RewriteResponse rewrite(RewriteRequest req, String token) { rewriteResponse.getRewriteToken(), rewriteResponse.getTotalBytesRewritten().longValue()); } catch (IOException ex) { + otelSpan.recordException(ex); + otelSpan.setStatus(StatusCode.ERROR, ex.getClass().getSimpleName()); tracer.getCurrentSpan().setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); throw translate(ex); + } finally { + otelSpan.end(); } } diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/ITHttpOpenTelemetryTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/ITHttpOpenTelemetryTest.java index c4b43fd7ad..afe7884445 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/ITHttpOpenTelemetryTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/ITHttpOpenTelemetryTest.java @@ -19,6 +19,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.cloud.NoCredentials; +import com.google.cloud.storage.Storage.BlobSourceOption; +import com.google.cloud.storage.Storage.BlobTargetOption; +import com.google.cloud.storage.Storage.CopyRequest; import com.google.cloud.storage.it.runner.StorageITRunner; import com.google.cloud.storage.it.runner.annotations.Backend; import com.google.cloud.storage.it.runner.annotations.Inject; @@ -31,6 +34,10 @@ import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.List; import org.junit.Assert; import org.junit.Before; @@ -43,7 +50,10 @@ public class ITHttpOpenTelemetryTest { @Inject public TestBench testBench; private StorageOptions options; private SpanExporter exporter; + private BlobId blobId; private Storage storage; + private static final byte[] helloWorldTextBytes = "hello world".getBytes(); + private static final byte[] helloWorldGzipBytes = TestUtils.gzipBytes(helloWorldTextBytes); @Inject public Generator generator; @Inject public BucketInfo testBucket; @@ -65,6 +75,8 @@ public void setUp() { .setOpenTelemetrySdk(openTelemetrySdk) .build(); storage = options.getService(); + String objectString = generator.randomObjectName(); + blobId = BlobId.of(testBucket.getName(), objectString); } @Test @@ -72,13 +84,8 @@ public void runCreateBucket() { String bucket = "random-bucket"; storage.create(BucketInfo.of(bucket)); TestExporter testExported = (TestExporter) exporter; - SpanData spanData = testExported.getExportedSpans().get(0); - Assert.assertEquals("Storage", getAttributeValue(spanData, "gcp.client.service")); - Assert.assertEquals("googleapis/java-storage", getAttributeValue(spanData, "gcp.client.repo")); - Assert.assertEquals( - "com.google.cloud.google-cloud-storage", - getAttributeValue(spanData, "gcp.client.artifact")); - Assert.assertEquals("http", getAttributeValue(spanData, "rpc.system")); + List spanData = testExported.getExportedSpans(); + checkCommonAttributes(spanData); } @Test @@ -88,6 +95,53 @@ public void runCreateBlob() { storage.create(BlobInfo.newBuilder(toCreate).build(), content); TestExporter testExported = (TestExporter) exporter; List spanData = testExported.getExportedSpans(); + checkCommonAttributes(spanData); + } + + @Test + public void runRead() throws IOException { + BlobInfo blobInfo = + BlobInfo.newBuilder(blobId).setContentEncoding("gzip").setContentType("text/plain").build(); + storage.create(blobInfo, helloWorldGzipBytes); + Path helloWorldTxtGz = File.createTempFile(blobId.getName(), ".txt.gz").toPath(); + storage.downloadTo( + blobId, helloWorldTxtGz, Storage.BlobSourceOption.shouldReturnRawInputStream(true)); + TestExporter testExported = (TestExporter) exporter; + List spanData = testExported.getExportedSpans(); + checkCommonAttributes(spanData); + Assert.assertTrue(spanData.stream().anyMatch(x -> x.getName().contains("read"))); + } + + @Test + public void runCopy() { + + byte[] expected = "Hello, World!".getBytes(StandardCharsets.UTF_8); + + BlobInfo info = + BlobInfo.newBuilder(testBucket.getName(), generator.randomObjectName() + "copy/src") + .build(); + Blob cpySrc = storage.create(info, expected, BlobTargetOption.doesNotExist()); + + BlobInfo dst = + BlobInfo.newBuilder(testBucket.getName(), generator.randomObjectName() + "copy/dst") + .build(); + + CopyRequest copyRequest = + CopyRequest.newBuilder() + .setSource(cpySrc.getBlobId()) + .setSourceOptions(BlobSourceOption.generationMatch(cpySrc.getGeneration())) + .setTarget(dst, BlobTargetOption.doesNotExist()) + .build(); + CopyWriter copyWriter = storage.copy(copyRequest); + copyWriter.getResult(); + TestExporter testExported = (TestExporter) exporter; + List spanData = testExported.getExportedSpans(); + checkCommonAttributes(spanData); + Assert.assertTrue(spanData.stream().anyMatch(x -> x.getName().contains("openRewrite"))); + Assert.assertTrue(spanData.stream().anyMatch(x -> x.getName().contains("rewrite"))); + } + + private void checkCommonAttributes(List spanData) { for (SpanData span : spanData) { Assert.assertEquals("Storage", getAttributeValue(span, "gcp.client.service")); Assert.assertEquals("googleapis/java-storage", getAttributeValue(span, "gcp.client.repo"));