diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 2c46c4642e56e..db9b789c1179f 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1551,6 +1551,16 @@ + + + + + + + + + + diff --git a/modules/repository-gcs/build.gradle b/modules/repository-gcs/build.gradle index d23a0f4a7e44d..39e1b9efcaa23 100644 --- a/modules/repository-gcs/build.gradle +++ b/modules/repository-gcs/build.gradle @@ -50,6 +50,9 @@ dependencies { api 'com.google.api:gax-httpjson:0.105.1' api 'io.grpc:grpc-context:1.49.2' api 'io.opencensus:opencensus-api:0.31.1' + api 'io.opencensus:opencensus-impl-core:0.31.1' + api 'io.opencensus:opencensus-impl-lite:0.31.1' + api 'io.opencensus:opencensus-contrib-http-util:0.31.1' api 'com.google.apis:google-api-services-storage:v1-rev20220705-2.0.0' diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GcpTracer.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GcpTracer.java new file mode 100644 index 0000000000000..f9165d500be85 --- /dev/null +++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GcpTracer.java @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.repositories.gcs; + +import io.opencensus.trace.Tracing; +import io.opencensus.trace.config.TraceConfig; +import io.opencensus.trace.export.SpanData; +import io.opencensus.trace.export.SpanExporter; +import io.opencensus.trace.samplers.Samplers; + +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; + +public class GcpTracer extends SpanExporter.Handler { + + private static final Logger logger = LogManager.getLogger(GcpTracer.class); + + public static void createAndRegister() { + Tracing.getExportComponent().getSpanExporter().registerHandler("gcp-tracer", new GcpTracer()); + TraceConfig traceConfig = Tracing.getTraceConfig(); + traceConfig.updateActiveTraceParams(traceConfig.getActiveTraceParams().toBuilder().setSampler(Samplers.alwaysSample()).build()); + } + + public static void shutdown() { + Tracing.getExportComponent().shutdown(); + } + + static String parentSpanId(SpanData sd) { + var ps = sd.getParentSpanId(); + return ps == null ? ".null" : ps.toLowerBase16(); + } + + static String traceId(SpanData sd) { + return sd.getContext().getTraceId().toLowerBase16(); + } + + @Override + public void export(Collection spanDataList) { + var out = new ArrayList<>(spanDataList); + out.sort(Comparator.comparing(GcpTracer::traceId).thenComparing(GcpTracer::parentSpanId)); + var sb = new StringBuilder(); + var f = "%32s\t%16s\t%16s\t%s\n"; + sb.append('\n'); + sb.append(String.format(f, "trace", "parent", "span", "name")); + for (var sd : out) { + sb.append(String.format(f, traceId(sd), parentSpanId(sd), sd.getContext().getSpanId().toLowerBase16(), sd.getName())); + } + logger.info(sb.toString()); + } +} diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobContainer.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobContainer.java index 3ed492881afa9..0c36ca5bddd67 100644 --- a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobContainer.java +++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobContainer.java @@ -9,6 +9,8 @@ package org.elasticsearch.repositories.gcs; +import io.opencensus.trace.Tracing; + import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.blobstore.BlobContainer; import org.elasticsearch.common.blobstore.BlobPath; @@ -76,12 +78,21 @@ public InputStream readBlob(OperationPurpose purpose, final String blobName, fin @Override public void writeBlob(OperationPurpose purpose, String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException { - blobStore.writeBlob(buildKey(blobName), inputStream, blobSize, failIfAlreadyExists); + withSpan(purpose, () -> blobStore.writeBlob(buildKey(blobName), inputStream, blobSize, failIfAlreadyExists)); } @Override public void writeBlob(OperationPurpose purpose, String blobName, BytesReference bytes, boolean failIfAlreadyExists) throws IOException { - blobStore.writeBlob(buildKey(blobName), bytes, failIfAlreadyExists); + withSpan(purpose, () -> blobStore.writeBlob(buildKey(blobName), bytes, failIfAlreadyExists)); + } + + private void withSpan(OperationPurpose purpose, IOThrowing runnable) throws IOException { + var span = Tracing.getTracer().spanBuilder(purpose.name()).startSpan(); + try (var ignored = Tracing.getTracer().withSpan(span)) { + runnable.run(); + } finally { + span.end(); + } } @Override @@ -106,6 +117,10 @@ public void writeBlobAtomic( writeBlob(purpose, blobName, inputStream, blobSize, failIfAlreadyExists); } + interface IOThrowing { + void run() throws IOException; + } + @Override public void writeBlobAtomic(OperationPurpose purpose, String blobName, BytesReference bytes, boolean failIfAlreadyExists) throws IOException { diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java index 14281b7288067..1188ced760cb2 100644 --- a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java +++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java @@ -22,6 +22,7 @@ import org.elasticsearch.repositories.Repository; import org.elasticsearch.xcontent.NamedXContentRegistry; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -92,4 +93,9 @@ public void reload(Settings settings) { final Map clientsSettings = GoogleCloudStorageClientSettings.load(settings); this.storageService.refreshAndClearCache(clientsSettings); } + + @Override + public void close() throws IOException { + this.storageService.shutdown(); + } } diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java index 6a4eeeeabbb6f..56a3efc07f3e9 100644 --- a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java +++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java @@ -47,6 +47,10 @@ public class GoogleCloudStorageService { + public GoogleCloudStorageService() { + GcpTracer.createAndRegister(); + } + private static final Logger logger = LogManager.getLogger(GoogleCloudStorageService.class); private volatile Map clientSettings = emptyMap(); @@ -85,6 +89,7 @@ public synchronized void refreshAndClearCache(Map