From fe5e51c371f8ceff406b04c1e09c08827712fd0c Mon Sep 17 00:00:00 2001 From: Surbhi Garg Date: Mon, 20 Apr 2026 14:29:01 +0530 Subject: [PATCH 1/2] feat(spanner): Update OpenTelemetry OTLP trace sample to use Google Cloud OTLP endpoint --- .../example/spanner/OpenTelemetryUsage.java | 113 +++++++++++------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java b/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java index 729207ed64b..1efb18bec16 100644 --- a/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java +++ b/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java @@ -16,6 +16,7 @@ package com.example.spanner; +import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.opentelemetry.trace.TraceConfiguration; import com.google.cloud.opentelemetry.trace.TraceExporter; import com.google.cloud.spanner.DatabaseClient; @@ -32,10 +33,13 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -/** - * This sample demonstrates how to configure OpenTelemetry and inject via Spanner Options. - */ +/** This sample demonstrates how to configure OpenTelemetry and inject via Spanner Options. */ public class OpenTelemetryUsage { static SdkTracerProvider sdkTracerProvider; @@ -46,11 +50,11 @@ public class OpenTelemetryUsage { static String instanceId = "my-instance"; static String databaseId = "my-database"; - // Replace these variables to use OTLP Exporter - static boolean useCloudTraceExporter = true; // Replace to false for OTLP - static String otlpEndpoint = "http://localhost:4317"; // Replace with your OTLP endpoint + static boolean useCloudTraceExporter = false; // Replace to true for Cloud Trace exporter + static String otlpEndpoint = + "https://telemetry.googleapis.com"; // Replace with your OTLP endpoint - public static void main(String[] args) { + public static void main(String[] args) throws IOException { if (useCloudTraceExporter) { spanner = getSpannerWithCloudTraceExporter(); @@ -58,8 +62,8 @@ public static void main(String[] args) { spanner = getSpannerWithOtlpExporter(); } - DatabaseClient dbClient = spanner - .getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + DatabaseClient dbClient = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); try (ResultSet resultSet = dbClient @@ -74,15 +78,37 @@ public static void main(String[] args) { sdkTracerProvider.forceFlush(); } - public static Spanner getSpannerWithOtlpExporter() { + public static Spanner getSpannerWithOtlpExporter() throws IOException { // [START spanner_opentelemetry_traces_otlp_usage] - Resource resource = Resource - .getDefault().merge(Resource.builder().put("service.name", "My App").build()); - + Resource resource = + Resource.getDefault() + .merge( + Resource.builder() + .put("service.name", "My App") + .put("gcp.project_id", projectId) + .build()); + + GoogleCredentials credentials = + GoogleCredentials.getApplicationDefault() + .createScoped(Collections.singleton("https://www.googleapis.com/auth/trace.append")); OtlpGrpcSpanExporter otlpGrpcSpanExporter = - OtlpGrpcSpanExporter - .builder() - .setEndpoint(otlpEndpoint) // Replace with your OTLP endpoint + OtlpGrpcSpanExporter.builder() + .setEndpoint(otlpEndpoint) + .setHeaders( + () -> { + try { + credentials.refreshIfExpired(); + Map> metadata = credentials.getRequestMetadata(); + Map headers = new HashMap<>(); + if (metadata != null) { + metadata.forEach((key, values) -> headers.put(key, String.join(",", values))); + } + return headers; + } catch (Exception e) { + // Handle error fetching credentials + return Collections.emptyMap(); + } + }) // Replace with your OTLP endpoint .build(); // Using a batch span processor @@ -92,24 +118,23 @@ public static Spanner getSpannerWithOtlpExporter() { BatchSpanProcessor.builder(otlpGrpcSpanExporter).build(); // Create a new tracer provider - sdkTracerProvider = SdkTracerProvider.builder() - // Use Otlp exporter or any other exporter of your choice. - .addSpanProcessor(otlpGrpcSpanProcessor) - .setResource(resource) - .setSampler(Sampler.traceIdRatioBased(0.1)) - .build(); + sdkTracerProvider = + SdkTracerProvider.builder() + // Use Otlp exporter or any other exporter of your choice. + .addSpanProcessor(otlpGrpcSpanProcessor) + .setResource(resource) + .setSampler(Sampler.traceIdRatioBased(0.1)) + .build(); // Export to a collector that is expecting OTLP using gRPC. - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() - .setTracerProvider(sdkTracerProvider).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); // Enable OpenTelemetry traces before Injecting OpenTelemetry SpannerOptions.enableOpenTelemetryTraces(); // Inject OpenTelemetry object via Spanner options or register as GlobalOpenTelemetry. - SpannerOptions options = SpannerOptions.newBuilder() - .setOpenTelemetry(openTelemetry) - .build(); + SpannerOptions options = SpannerOptions.newBuilder().setOpenTelemetry(openTelemetry).build(); Spanner spanner = options.getService(); // [END spanner_opentelemetry_traces_otlp_usage] @@ -118,30 +143,30 @@ public static Spanner getSpannerWithOtlpExporter() { public static Spanner getSpannerWithCloudTraceExporter() { // [START spanner_opentelemetry_traces_cloudtrace_usage] - Resource resource = Resource - .getDefault().merge(Resource.builder().put("service.name", "My App").build()); + Resource resource = + Resource.getDefault().merge(Resource.builder().put("service.name", "My App").build()); - SpanExporter traceExporter = TraceExporter.createWithConfiguration( - TraceConfiguration.builder().setProjectId(projectId).build() - ); + SpanExporter traceExporter = + TraceExporter.createWithConfiguration( + TraceConfiguration.builder().setProjectId(projectId).build()); // Using a batch span processor // You can use `.setScheduleDelay()`, `.setExporterTimeout()`, // `.setMaxQueueSize`(), and `.setMaxExportBatchSize()` to further customize. - BatchSpanProcessor otlpGrpcSpanProcessor = - BatchSpanProcessor.builder(traceExporter).build(); + BatchSpanProcessor otlpGrpcSpanProcessor = BatchSpanProcessor.builder(traceExporter).build(); // Create a new tracer provider - sdkTracerProvider = SdkTracerProvider.builder() - // Use Otlp exporter or any other exporter of your choice. - .addSpanProcessor(otlpGrpcSpanProcessor) - .setResource(resource) - .setSampler(Sampler.traceIdRatioBased(0.1)) - .build(); + sdkTracerProvider = + SdkTracerProvider.builder() + // Use Otlp exporter or any other exporter of your choice. + .addSpanProcessor(otlpGrpcSpanProcessor) + .setResource(resource) + .setSampler(Sampler.traceIdRatioBased(0.1)) + .build(); // Export to a collector that is expecting OTLP using gRPC. - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() - .setTracerProvider(sdkTracerProvider).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); // Enable OpenTelemetry traces before Injecting OpenTelemetry SpannerOptions.enableOpenTelemetryTraces(); @@ -149,9 +174,7 @@ public static Spanner getSpannerWithCloudTraceExporter() { // Inject OpenTelemetry object via Spanner options or register it as global object. // To register as the global OpenTelemetry object, // use "OpenTelemetrySdk.builder()....buildAndRegisterGlobal()". - SpannerOptions options = SpannerOptions.newBuilder() - .setOpenTelemetry(openTelemetry) - .build(); + SpannerOptions options = SpannerOptions.newBuilder().setOpenTelemetry(openTelemetry).build(); Spanner spanner = options.getService(); // [END spanner_opentelemetry_traces_cloudtrace_usage] From 4a7c096414cff19528d65c6c4c69922c574a38b1 Mon Sep 17 00:00:00 2001 From: Surbhi Garg Date: Tue, 21 Apr 2026 15:20:58 +0530 Subject: [PATCH 2/2] review comments --- .../example/spanner/OpenTelemetryUsage.java | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java b/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java index 1efb18bec16..f43040362e6 100644 --- a/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java +++ b/spanner/opentelemetry_traces/src/main/java/com/example/spanner/OpenTelemetryUsage.java @@ -27,6 +27,7 @@ import com.google.cloud.spanner.Statement; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; @@ -34,8 +35,8 @@ import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.io.IOException; +import java.net.URI; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,9 +51,21 @@ public class OpenTelemetryUsage { static String instanceId = "my-instance"; static String databaseId = "my-database"; - static boolean useCloudTraceExporter = false; // Replace to true for Cloud Trace exporter - static String otlpEndpoint = - "https://telemetry.googleapis.com"; // Replace with your OTLP endpoint + // Use the OTLP exporter by default. Set to true to use the Cloud Trace exporter instead. + static boolean useCloudTraceExporter = false; + + // The OTLP endpoint to send traces to. + // It is recommended to use environment variables (OTEL_EXPORTER_OTLP_ENDPOINT) for configuration. + static String otlpEndpoint = System.getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"); + + static { + if (otlpEndpoint == null) { + otlpEndpoint = System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"); + } + if (otlpEndpoint == null) { + otlpEndpoint = "https://telemetry.googleapis.com"; // Google OTLP gRPC endpoint + } + } public static void main(String[] args) throws IOException { @@ -81,52 +94,31 @@ public static void main(String[] args) throws IOException { public static Spanner getSpannerWithOtlpExporter() throws IOException { // [START spanner_opentelemetry_traces_otlp_usage] Resource resource = - Resource.getDefault() - .merge( - Resource.builder() - .put("service.name", "My App") - .put("gcp.project_id", projectId) - .build()); - - GoogleCredentials credentials = - GoogleCredentials.getApplicationDefault() - .createScoped(Collections.singleton("https://www.googleapis.com/auth/trace.append")); - OtlpGrpcSpanExporter otlpGrpcSpanExporter = - OtlpGrpcSpanExporter.builder() - .setEndpoint(otlpEndpoint) - .setHeaders( - () -> { - try { - credentials.refreshIfExpired(); - Map> metadata = credentials.getRequestMetadata(); - Map headers = new HashMap<>(); - if (metadata != null) { - metadata.forEach((key, values) -> headers.put(key, String.join(",", values))); - } - return headers; - } catch (Exception e) { - // Handle error fetching credentials - return Collections.emptyMap(); - } - }) // Replace with your OTLP endpoint - .build(); - - // Using a batch span processor - // You can use `.setScheduleDelay()`, `.setExporterTimeout()`, - // `.setMaxQueueSize`(), and `.setMaxExportBatchSize()` to further customize. - BatchSpanProcessor otlpGrpcSpanProcessor = - BatchSpanProcessor.builder(otlpGrpcSpanExporter).build(); + Resource.getDefault().merge(Resource.builder().put("gcp.project_id", projectId).build()); + + OtlpGrpcSpanExporterBuilder exporterBuilder = + OtlpGrpcSpanExporter.builder().setEndpoint(otlpEndpoint); + + // Add authentication only if the endpoint is the Google Cloud Telemetry API. + // The standard endpoint is telemetry.googleapis.com + if (otlpEndpoint.contains("telemetry.googleapis.com")) { + GoogleCredentials credentials = + GoogleCredentials.getApplicationDefault() + .createScoped(Collections.singleton("https://www.googleapis.com/auth/trace.append")); + credentials.refreshIfExpired(); + Map> metadata = credentials.getRequestMetadata(URI.create(otlpEndpoint)); + if (metadata != null) { + metadata.forEach((key, values) -> exporterBuilder.addHeader(key, String.join(",", values))); + } + } - // Create a new tracer provider sdkTracerProvider = SdkTracerProvider.builder() - // Use Otlp exporter or any other exporter of your choice. - .addSpanProcessor(otlpGrpcSpanProcessor) + .addSpanProcessor(BatchSpanProcessor.builder(exporterBuilder.build()).build()) .setResource(resource) .setSampler(Sampler.traceIdRatioBased(0.1)) .build(); - // Export to a collector that is expecting OTLP using gRPC. OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); @@ -135,10 +127,8 @@ public static Spanner getSpannerWithOtlpExporter() throws IOException { // Inject OpenTelemetry object via Spanner options or register as GlobalOpenTelemetry. SpannerOptions options = SpannerOptions.newBuilder().setOpenTelemetry(openTelemetry).build(); - Spanner spanner = options.getService(); + return options.getService(); // [END spanner_opentelemetry_traces_otlp_usage] - - return spanner; } public static Spanner getSpannerWithCloudTraceExporter() {