From e01ca0675fd100b29ef44d615b69bc92cfa3790b Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 13:37:43 -0500 Subject: [PATCH 1/9] feat(observability): add no-op implementation of tracing --- .../api/gax/tracing/TracingRecorder.java | 44 +++++++++++ .../google/api/gax/tracing/TracingTracer.java | 47 +++++++++++ .../api/gax/tracing/TracingTracerFactory.java | 55 +++++++++++++ .../google/api/gax/tracing/TracingUtils.java | 59 ++++++++++++++ .../gax/tracing/TracingTracerFactoryTest.java | 79 +++++++++++++++++++ .../api/gax/tracing/TracingTracerTest.java | 46 +++++++++++ .../api/gax/tracing/TracingUtilsTest.java | 56 +++++++++++++ .../showcase/v1beta1/it/ITOtelMetrics.java | 32 +++++++- 8 files changed, 414 insertions(+), 4 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingUtils.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerFactoryTest.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerTest.java create mode 100644 gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingUtilsTest.java diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java new file mode 100644 index 0000000000..2d66129d2d --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java @@ -0,0 +1,44 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import com.google.api.core.BetaApi; +import com.google.api.core.InternalApi; + +/** + * Provides an interface for tracing recording. The implementer is expected to use an observability + * framework, e.g. OpenTelemetry. + */ +@BetaApi +@InternalApi +public interface TracingRecorder { + // Minimal working feature: noop implementation for now. +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java new file mode 100644 index 0000000000..4bd9d4e08a --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import com.google.api.core.BetaApi; +import com.google.api.core.InternalApi; + +/** + * This class computes generic traces that can be observed in the lifecycle of an RPC operation. + */ +@BetaApi +@InternalApi +public class TracingTracer extends BaseApiTracer { + private final TracingRecorder tracingRecorder; + + public TracingTracer(TracingRecorder tracingRecorder) { + this.tracingRecorder = tracingRecorder; + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java new file mode 100644 index 0000000000..85bddf37f6 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java @@ -0,0 +1,55 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import com.google.api.core.BetaApi; +import com.google.api.core.InternalApi; + +/** + * A {@link ApiTracerFactory} to build instances of {@link TracingTracer}. + */ +@BetaApi +@InternalApi +public class TracingTracerFactory implements ApiTracerFactory { + private final TracingRecorder tracingRecorder; + + public TracingTracerFactory(TracingRecorder tracingRecorder) { + this.tracingRecorder = tracingRecorder; + } + + @Override + public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) { + if (!TracingUtils.isTracingEnabled()) { + return BaseApiTracer.getInstance(); + } + return new TracingTracer(tracingRecorder); + } +} diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingUtils.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingUtils.java new file mode 100644 index 0000000000..161f93f558 --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import com.google.api.core.InternalApi; +import com.google.common.annotations.VisibleForTesting; + +/** Internal utility class to manage tracing configuration. */ +@InternalApi +public class TracingUtils { + static final String GOOGLE_CLOUD_ENABLE_TRACING = "GOOGLE_CLOUD_ENABLE_TRACING"; + + private TracingUtils() {} + + /** + * Returns true if tracing is enabled via the GOOGLE_CLOUD_ENABLE_TRACING environment variable or + * system property. + */ + public static boolean isTracingEnabled() { + String enableTracing = System.getProperty(GOOGLE_CLOUD_ENABLE_TRACING); + if (enableTracing == null) { + enableTracing = System.getenv(GOOGLE_CLOUD_ENABLE_TRACING); + } + return isTracingEnabled(enableTracing); + } + + @VisibleForTesting + static boolean isTracingEnabled(String envValue) { + return "true".equalsIgnoreCase(envValue); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerFactoryTest.java b/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerFactoryTest.java new file mode 100644 index 0000000000..9169b14abd --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerFactoryTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + +import com.google.api.gax.tracing.ApiTracerFactory.OperationType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TracingTracerFactoryTest { + private TracingRecorder tracingRecorder; + private TracingTracerFactory factory; + private ApiTracer parent; + private SpanName spanName; + + @BeforeEach + void setUp() { + tracingRecorder = mock(TracingRecorder.class); + factory = new TracingTracerFactory(tracingRecorder); + parent = mock(ApiTracer.class); + spanName = mock(SpanName.class); + } + + @AfterEach + void tearDown() { + System.clearProperty("GOOGLE_CLOUD_ENABLE_TRACING"); + } + + @Test + void testNewTracer_enabled() { + System.setProperty("GOOGLE_CLOUD_ENABLE_TRACING", "true"); + ApiTracer tracer = factory.newTracer(parent, spanName, OperationType.Unary); + assertThat(tracer).isInstanceOf(TracingTracer.class); + } + + @Test + void testNewTracer_disabled() { + System.setProperty("GOOGLE_CLOUD_ENABLE_TRACING", "false"); + ApiTracer tracer = factory.newTracer(parent, spanName, OperationType.Unary); + assertThat(tracer).isSameInstanceAs(BaseApiTracer.getInstance()); + } + + @Test + void testNewTracer_defaultDisabled() { + ApiTracer tracer = factory.newTracer(parent, spanName, OperationType.Unary); + assertThat(tracer).isSameInstanceAs(BaseApiTracer.getInstance()); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerTest.java b/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerTest.java new file mode 100644 index 0000000000..f8506d9f06 --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingTracerTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class TracingTracerTest { + + @Test + void testConstructor() { + TracingRecorder recorder = Mockito.mock(TracingRecorder.class); + TracingTracer tracer = new TracingTracer(recorder); + assertThat(tracer).isNotNull(); + } +} diff --git a/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingUtilsTest.java b/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingUtilsTest.java new file mode 100644 index 0000000000..9449fc571f --- /dev/null +++ b/gax-java/gax/src/test/java/com/google/api/gax/tracing/TracingUtilsTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.jupiter.api.Test; + +class TracingUtilsTest { + + @Test + void testIsTracingEnabled_defaultToFalse() { + assertThat(TracingUtils.isTracingEnabled(null)).isFalse(); + } + + @Test + void testIsTracingEnabled_true() { + assertThat(TracingUtils.isTracingEnabled("true")).isTrue(); + assertThat(TracingUtils.isTracingEnabled("TRUE")).isTrue(); + } + + @Test + void testIsTracingEnabled_false() { + assertThat(TracingUtils.isTracingEnabled("false")).isFalse(); + assertThat(TracingUtils.isTracingEnabled("random")).isFalse(); + assertThat(TracingUtils.isTracingEnabled("")).isFalse(); + } +} diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java index 06f46deee8..25300f9246 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2026 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -30,6 +30,7 @@ package com.google.showcase.v1beta1.it; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import com.google.api.client.http.javanet.NetHttpTransport; @@ -42,9 +43,7 @@ import com.google.api.gax.rpc.StatusCode.Code; import com.google.api.gax.rpc.UnaryCallable; import com.google.api.gax.rpc.UnavailableException; -import com.google.api.gax.tracing.MetricsTracer; -import com.google.api.gax.tracing.MetricsTracerFactory; -import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.api.gax.tracing.*; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -167,6 +166,8 @@ void setup() throws Exception { @AfterEach void cleanup() throws InterruptedException, IOException { + System.clearProperty("GOOGLE_CLOUD_ENABLE_TRACING"); + inMemoryMetricReader.close(); inMemoryMetricReader.shutdown(); @@ -919,4 +920,27 @@ void grpcOpenTelemetryImplementation_setSampledToLocalTracing_methodFullNameIsRe echoClient.close(); echoClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } + + @Test + void testTracingFeatureFlag() { + // Test tracing disabled + System.setProperty("GOOGLE_CLOUD_ENABLE_TRACING", "false"); + TracingTracerFactory factory = new TracingTracerFactory(null); + ApiTracer tracer = + factory.newTracer( + BaseApiTracer.getInstance(), + SpanName.of("EchoClient", "Echo"), + ApiTracerFactory.OperationType.Unary); + assertThat(tracer).isNotInstanceOf(TracingTracer.class); + assertThat(tracer).isSameInstanceAs(BaseApiTracer.getInstance()); + + // Test tracing enabled + System.setProperty("GOOGLE_CLOUD_ENABLE_TRACING", "true"); + tracer = + factory.newTracer( + BaseApiTracer.getInstance(), + SpanName.of("EchoClient", "Echo"), + ApiTracerFactory.OperationType.Unary); + assertThat(tracer).isInstanceOf(TracingTracer.class); + } } From d3f61d1c254c2a5ed44d6ec8530b16882bce0428 Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 13:52:11 -0500 Subject: [PATCH 2/9] chore: add otel tracing recorder --- .../tracing/OpenTelemetryTracingRecorder.java | 50 +++++++++++++++++++ .../showcase/v1beta1/it/ITOtelMetrics.java | 15 ++++-- 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java new file mode 100644 index 0000000000..91b403195b --- /dev/null +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java @@ -0,0 +1,50 @@ +/* + * Copyright 2026 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.tracing; + +import com.google.api.core.BetaApi; +import com.google.api.core.InternalApi; +import io.opentelemetry.api.OpenTelemetry; + +/** + * OpenTelemetry implementation of recording traces. + */ +@BetaApi +@InternalApi +public class OpenTelemetryTracingRecorder implements TracingRecorder { + private final OpenTelemetry openTelemetry; + private final String serviceName; + + public OpenTelemetryTracingRecorder(OpenTelemetry openTelemetry, String serviceName) { + this.openTelemetry = openTelemetry; + this.serviceName = serviceName; + } +} diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java index 25300f9246..95bbb59575 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java @@ -144,13 +144,17 @@ public int getCount() { private OpenTelemetryMetricsRecorder createOtelMetricsRecorder( InMemoryMetricReader inMemoryMetricReader) { SdkMeterProvider sdkMeterProvider = - SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); - - OpenTelemetry openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); return new OpenTelemetryMetricsRecorder(openTelemetry, SERVICE_NAME); } + private OpenTelemetryTracingRecorder createOtelTracingRecorder() { + SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder().build(); + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); + return new OpenTelemetryTracingRecorder(openTelemetry, SERVICE_NAME); + } + @BeforeEach void setup() throws Exception { inMemoryMetricReader = InMemoryMetricReader.create(); @@ -925,7 +929,8 @@ void grpcOpenTelemetryImplementation_setSampledToLocalTracing_methodFullNameIsRe void testTracingFeatureFlag() { // Test tracing disabled System.setProperty("GOOGLE_CLOUD_ENABLE_TRACING", "false"); - TracingTracerFactory factory = new TracingTracerFactory(null); + TracingRecorder recorder = createOtelTracingRecorder(); + TracingTracerFactory factory = new TracingTracerFactory(recorder); ApiTracer tracer = factory.newTracer( BaseApiTracer.getInstance(), From cf98e86e6588b533e3033a8699c9f2c0cc2c8a1b Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 13:58:29 -0500 Subject: [PATCH 3/9] chore: add javadocs --- .../api/gax/tracing/OpenTelemetryTracingRecorder.java | 9 ++++++++- .../java/com/google/api/gax/tracing/TracingRecorder.java | 4 +++- .../java/com/google/api/gax/tracing/TracingTracer.java | 4 +++- .../com/google/api/gax/tracing/TracingTracerFactory.java | 5 +++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java index 91b403195b..6f32088cb0 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java @@ -35,7 +35,8 @@ import io.opentelemetry.api.OpenTelemetry; /** - * OpenTelemetry implementation of recording traces. + * OpenTelemetry implementation of recording traces. This implementation collections the + * measurements related to the lifecyle of an RPC. */ @BetaApi @InternalApi @@ -43,6 +44,12 @@ public class OpenTelemetryTracingRecorder implements TracingRecorder { private final OpenTelemetry openTelemetry; private final String serviceName; + /** + * Creates a new instance of OpenTelemetryTracingRecorder. + * + * @param openTelemetry OpenTelemetry instance + * @param serviceName Service Name + */ public OpenTelemetryTracingRecorder(OpenTelemetry openTelemetry, String serviceName) { this.openTelemetry = openTelemetry; this.serviceName = serviceName; diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java index 2d66129d2d..afb7ff6078 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingRecorder.java @@ -35,7 +35,9 @@ /** * Provides an interface for tracing recording. The implementer is expected to use an observability - * framework, e.g. OpenTelemetry. + * framework, e.g. OpenTelemetry. There should be only one instance of TracingRecorder per client, + * all the methods in this class are expected to be called from multiple threads, hence the + * implementation must be thread safe. */ @BetaApi @InternalApi diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java index 4bd9d4e08a..c5ea38a9b1 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracer.java @@ -34,7 +34,9 @@ import com.google.api.core.InternalApi; /** - * This class computes generic traces that can be observed in the lifecycle of an RPC operation. + * This class computes generic traces that can be observed in the lifecycle of an RPC operation. The + * responsibility of recording traces should delegate to {@link TracingRecorder}, hence this class + * should not have any knowledge about the observability framework used for tracing recording. */ @BetaApi @InternalApi diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java index 85bddf37f6..8ea6a3e6b6 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java @@ -35,6 +35,11 @@ /** * A {@link ApiTracerFactory} to build instances of {@link TracingTracer}. + * + *

This class wraps the {@link TracingRecorder} and pass it to {@link TracingTracer}. It will be + * used to record traces in {@link TracingTracer}. + * + *

This class is expected to be initialized once during client initialization. */ @BetaApi @InternalApi From 523c3011e8d270064a4961639dcfc73e54349392 Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 14:09:07 -0500 Subject: [PATCH 4/9] chore: adjust tests and conform more closely to metrics counterpart. --- .../api/gax/tracing/TracingTracerFactory.java | 17 ++++++++++++++++- .../showcase/v1beta1/it/ITOtelMetrics.java | 8 +++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java index 8ea6a3e6b6..750947b67d 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/TracingTracerFactory.java @@ -32,6 +32,8 @@ import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; +import com.google.common.collect.ImmutableMap; +import java.util.Map; /** * A {@link ApiTracerFactory} to build instances of {@link TracingTracer}. @@ -44,10 +46,23 @@ @BetaApi @InternalApi public class TracingTracerFactory implements ApiTracerFactory { - private final TracingRecorder tracingRecorder; + protected TracingRecorder tracingRecorder; + /** Mapping of client attributes that are set for every TracingTracer */ + private final Map attributes; + + /** Creates a TracingTracerFactory with no additional client level attributes. */ public TracingTracerFactory(TracingRecorder tracingRecorder) { + this(tracingRecorder, ImmutableMap.of()); + } + + /** + * Pass in a Map of client level attributes which will be added to every single TracingTracer + * created from the ApiTracerFactory. + */ + public TracingTracerFactory(TracingRecorder tracingRecorder, Map attributes) { this.tracingRecorder = tracingRecorder; + this.attributes = ImmutableMap.copyOf(attributes); } @Override diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java index 95bbb59575..f1fad60dca 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java @@ -144,14 +144,16 @@ public int getCount() { private OpenTelemetryMetricsRecorder createOtelMetricsRecorder( InMemoryMetricReader inMemoryMetricReader) { SdkMeterProvider sdkMeterProvider = - SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); return new OpenTelemetryMetricsRecorder(openTelemetry, SERVICE_NAME); } private OpenTelemetryTracingRecorder createOtelTracingRecorder() { SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder().build(); - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); return new OpenTelemetryTracingRecorder(openTelemetry, SERVICE_NAME); } From d9534bd344dcad3730ea7168391cd03a0bf5e85a Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 14:17:43 -0500 Subject: [PATCH 5/9] chore: expand imports in otel it --- .../google/showcase/v1beta1/it/ITOtelMetrics.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java index f1fad60dca..ca6b8e2914 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java @@ -43,7 +43,17 @@ import com.google.api.gax.rpc.StatusCode.Code; import com.google.api.gax.rpc.UnaryCallable; import com.google.api.gax.rpc.UnavailableException; -import com.google.api.gax.tracing.*; +import com.google.api.gax.tracing.ApiTracer; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.api.gax.tracing.BaseApiTracer; +import com.google.api.gax.tracing.MetricsTracer; +import com.google.api.gax.tracing.MetricsTracerFactory; +import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.api.gax.tracing.OpenTelemetryTracingRecorder; +import com.google.api.gax.tracing.SpanName; +import com.google.api.gax.tracing.TracingRecorder; +import com.google.api.gax.tracing.TracingTracer; +import com.google.api.gax.tracing.TracingTracerFactory; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; From 196d591390e93f584e2d02ba4f8888e33c5ff5ef Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 14:18:02 -0500 Subject: [PATCH 6/9] Update gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../google/api/gax/tracing/OpenTelemetryTracingRecorder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java index 6f32088cb0..440e3ec676 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java @@ -35,7 +35,7 @@ import io.opentelemetry.api.OpenTelemetry; /** - * OpenTelemetry implementation of recording traces. This implementation collections the + * OpenTelemetry implementation of recording traces. This implementation collects the * measurements related to the lifecyle of an RPC. */ @BetaApi From ea075d35ab910ad7a4bfe537524b429533d8486b Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 14:21:28 -0500 Subject: [PATCH 7/9] test: use tracer provider in ItOtelMetrics --- .../java/com/google/showcase/v1beta1/it/ITOtelMetrics.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java index ca6b8e2914..f601163218 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java @@ -92,6 +92,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; + +import io.opentelemetry.sdk.trace.SdkTracerProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -161,9 +163,9 @@ private OpenTelemetryMetricsRecorder createOtelMetricsRecorder( } private OpenTelemetryTracingRecorder createOtelTracingRecorder() { - SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder().build(); + SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder().build(); OpenTelemetry openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); + OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).build(); return new OpenTelemetryTracingRecorder(openTelemetry, SERVICE_NAME); } From 2c2697101d21455040ce92130c90b5750c049f22 Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Thu, 8 Jan 2026 14:33:38 -0500 Subject: [PATCH 8/9] chore: format --- .../google/api/gax/tracing/OpenTelemetryTracingRecorder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java index 440e3ec676..9d3409c3bc 100644 --- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java +++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/OpenTelemetryTracingRecorder.java @@ -35,8 +35,8 @@ import io.opentelemetry.api.OpenTelemetry; /** - * OpenTelemetry implementation of recording traces. This implementation collects the - * measurements related to the lifecyle of an RPC. + * OpenTelemetry implementation of recording traces. This implementation collects the measurements + * related to the lifecyle of an RPC. */ @BetaApi @InternalApi From 1a76c29247e6b9cfaca1b69bf6e9bcdcc6387a34 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Thu, 8 Jan 2026 19:41:36 +0000 Subject: [PATCH 9/9] chore: generate libraries at Thu Jan 8 19:39:27 UTC 2026 --- .../java/com/google/showcase/v1beta1/it/ITOtelMetrics.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java index f601163218..8fb7348851 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java @@ -81,6 +81,7 @@ import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.data.PointData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import io.opentelemetry.sdk.trace.SdkTracerProvider; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -92,8 +93,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; - -import io.opentelemetry.sdk.trace.SdkTracerProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach;