From 87f60277fd170d6064986a80d98d7a608b04c313 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 31 Jul 2026 13:25:11 -0700 Subject: [PATCH 1/9] feat(generator): add enable_telemetry_tracing option and client interface field --- core/packages/gax/src/clientInterface.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/packages/gax/src/clientInterface.ts b/core/packages/gax/src/clientInterface.ts index b9f3bd8b8e9c..165626715867 100644 --- a/core/packages/gax/src/clientInterface.ts +++ b/core/packages/gax/src/clientInterface.ts @@ -42,6 +42,8 @@ export interface ClientOptions // No preference; exception will be thrown if both are set to different values. universeDomain?: string; universe_domain?: string; + enableTelemetryTracing?: boolean; + internalTelemetryInfo?: InternalTelemetry; } export interface Descriptors { @@ -93,3 +95,10 @@ export interface PaginationResponse< nextPageRequest?: RequestObject; rawResponse?: ResponseObject; } + +export interface InternalTelemetry { + gcpClientService?: string; + gcpVersion?: string; + gcpRepo?: string; + gcpArtifact?: string; +} \ No newline at end of file From 6e156ef3c84a225be59b4b619339fff962f7df02 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Mon, 3 Aug 2026 18:23:24 -0700 Subject: [PATCH 2/9] feat(gax): accept clientOptions in createApiCall for telemetry tracing --- core/packages/gax/src/createApiCall.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/packages/gax/src/createApiCall.ts b/core/packages/gax/src/createApiCall.ts index b3fdee987a6d..7b3bdb712a91 100644 --- a/core/packages/gax/src/createApiCall.ts +++ b/core/packages/gax/src/createApiCall.ts @@ -27,6 +27,7 @@ import { RequestType, SimpleCallbackFunction, } from './apitypes'; +import { ClientOptions } from './clientInterface'; import {Descriptor} from './descriptor'; import {CallOptions, CallSettings, convertRetryOptions} from './gax'; import {retryable} from './normalCalls/retries'; @@ -59,6 +60,7 @@ export function createApiCall( descriptor?: Descriptor, // eslint-disable-next-line @typescript-eslint/no-unused-vars _fallback?: boolean | 'proto' | 'rest', // unused here, used in fallback.ts implementation + clientOptions?: ClientOptions ): GaxCall { // we want to be able to accept both promise resolving to a function and a // function. Currently client librares are only calling this method with a @@ -67,6 +69,7 @@ export function createApiCall( // the following apiCaller will be used for all calls of this function... const apiCaller = createAPICaller(settings, descriptor); + const tracingEnabled = clientOptions?.enableTelemetryTracing && process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED; return ( request: RequestType, callOptions?: CallOptions, From 6bc747409abfc729b7dbcf73b1fb61043719e1d5 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Mon, 3 Aug 2026 18:31:44 -0700 Subject: [PATCH 3/9] feat(gax): check internalTelemetryInfo for tracingEnabled in createApiCall --- core/packages/gax/src/createApiCall.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/packages/gax/src/createApiCall.ts b/core/packages/gax/src/createApiCall.ts index 7b3bdb712a91..f74ba17fb81f 100644 --- a/core/packages/gax/src/createApiCall.ts +++ b/core/packages/gax/src/createApiCall.ts @@ -69,8 +69,9 @@ export function createApiCall( // the following apiCaller will be used for all calls of this function... const apiCaller = createAPICaller(settings, descriptor); - const tracingEnabled = clientOptions?.enableTelemetryTracing && process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED; - return ( + // Check if telemetry tracing is enabled and also check if internal telemetry information has been passed through the templates + const tracingEnabled = clientOptions?.enableTelemetryTracing && process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED && clientOptions?.internalTelemetryInfo !== undefined; + const invokeCall = ( request: RequestType, callOptions?: CallOptions, callback?: APICallback, @@ -167,4 +168,9 @@ export function createApiCall( // or to cancel the ongoing call. return currentApiCaller.result(ongoingCall); }; + if (tracingEnabled) { + return invokeCall; + } else { + return invokeCall; + } } From 5ecd8a9231c9f964506c8170070238bf07a463d3 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Mon, 3 Aug 2026 16:43:07 -0700 Subject: [PATCH 4/9] feat(generator): import getGaxTracer when enableTelemetryTracing is set --- .../cjs/typescript_gapic/src/$version/$service_client.ts.njk | 1 + .../esm/typescript_gapic/esm/src/$version/$service_client.ts.njk | 1 + 2 files changed, 2 insertions(+) diff --git a/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk b/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk index 90553e89770f..24f467204106 100644 --- a/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk +++ b/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk @@ -55,6 +55,7 @@ import {loggingUtils as logging} from 'google-gax'; import jsonProtos = require('../../protos/protos.json'); import {loggingUtils as logging, decodeAnyProtosInArray} from 'google-gax'; {%- endif %} +{% if api.enableTelemetryTracing %} import { getGaxTracer } from 'google-gax'; {% endif %} /** * Client JSON configuration object, loaded from diff --git a/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk b/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk index e505db0eef11..71bd062a9ff5 100644 --- a/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk +++ b/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk @@ -50,6 +50,7 @@ import {fileURLToPath} from 'url'; import {getJSON} from '../json-helper.cjs'; // @ts-ignore const dirname = path.dirname(fileURLToPath(import.meta.url)); +{% if api.enableTelemetryTracing %} import { getGaxTracer } from 'google-gax'; {% endif %} /** * Client JSON configuration object, loaded from From c1f66f0abc9290398a7e9cec54c44b660b9692ff Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Mon, 3 Aug 2026 18:03:19 -0700 Subject: [PATCH 5/9] feat(generator): add internalTelemetryInfo configuration when enableTelemetryTracing is enabled --- .../src/$version/$service_client.ts.njk | 10 +++++++++- .../esm/src/$version/$service_client.ts.njk | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk b/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk index 24f467204106..6b15efaa0cb9 100644 --- a/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk +++ b/core/generator/gapic-generator-typescript/templates/cjs/typescript_gapic/src/$version/$service_client.ts.njk @@ -55,7 +55,6 @@ import {loggingUtils as logging} from 'google-gax'; import jsonProtos = require('../../protos/protos.json'); import {loggingUtils as logging, decodeAnyProtosInArray} from 'google-gax'; {%- endif %} -{% if api.enableTelemetryTracing %} import { getGaxTracer } from 'google-gax'; {% endif %} /** * Client JSON configuration object, loaded from @@ -198,6 +197,15 @@ export class {{ service.name }}Client { } {%- endif %} + {%- if api.enableTelemetryTracing %} + opts.internalTelemetryInfo = { + gcpClientService: '{{ api.loggingName }}', + gcpClientVersion: '{{ api.naming.version }}', + gcpRepo: 'googleapis/google-cloud-node', + gcpArtifact: '{{ api.publishName }}', + } + {%- endif %} + // Choose either gRPC or proto-over-HTTP implementation of google-gax. this._gaxModule = {% if not api.legacyProtoLoad %}opts.fallback ? gaxInstance.fallback : gaxInstance{% else %}gax{% endif %}; diff --git a/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk b/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk index 71bd062a9ff5..886198d4e43d 100644 --- a/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk +++ b/core/generator/gapic-generator-typescript/templates/esm/typescript_gapic/esm/src/$version/$service_client.ts.njk @@ -50,7 +50,6 @@ import {fileURLToPath} from 'url'; import {getJSON} from '../json-helper.cjs'; // @ts-ignore const dirname = path.dirname(fileURLToPath(import.meta.url)); -{% if api.enableTelemetryTracing %} import { getGaxTracer } from 'google-gax'; {% endif %} /** * Client JSON configuration object, loaded from @@ -204,6 +203,15 @@ export class {{ service.name }}Client { gaxInstance = gax as typeof gax; } {%- endif %} + + {%- if api.enableTelemetryTracing %} + opts.internalTelemetryInfo = { + gcpClientService: '{{ api.loggingName }}', + gcpClientVersion: '{{ api.naming.version }}', + gcpRepo: 'googleapis/google-cloud-node', + gcpArtifact: '{{ api.publishName }}', + } + {%- endif %} // Choose either gRPC or proto-over-HTTP implementation of google-gax. this._gaxModule = {% if not api.legacyProtoLoad %}opts.fallback ? gaxInstance.fallback : gaxInstance{% else %}gax{% endif %}; From 9a210e559d427dcaf99b5949bed6e4b80a156bcd Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Tue, 21 Jul 2026 12:06:47 -0700 Subject: [PATCH 6/9] refactor(gax): extract invokeCall and return conditionally based on tracingEnabled --- core/packages/gax/src/createApiCall.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/packages/gax/src/createApiCall.ts b/core/packages/gax/src/createApiCall.ts index f74ba17fb81f..232bf3916f07 100644 --- a/core/packages/gax/src/createApiCall.ts +++ b/core/packages/gax/src/createApiCall.ts @@ -70,7 +70,10 @@ export function createApiCall( const apiCaller = createAPICaller(settings, descriptor); // Check if telemetry tracing is enabled and also check if internal telemetry information has been passed through the templates - const tracingEnabled = clientOptions?.enableTelemetryTracing && process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED && clientOptions?.internalTelemetryInfo !== undefined; + const tracingEnabled = + clientOptions?.enableTelemetryTracing && + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED && + clientOptions?.internalTelemetryInfo !== undefined; const invokeCall = ( request: RequestType, callOptions?: CallOptions, @@ -168,6 +171,7 @@ export function createApiCall( // or to cancel the ongoing call. return currentApiCaller.result(ongoingCall); }; + if (tracingEnabled) { return invokeCall; } else { From 28ae7fc5ab682c4a8c056cda31f7ce0f1c6bcae5 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Wed, 22 Jul 2026 13:38:28 -0700 Subject: [PATCH 7/9] fix(gax): gate OpenTelemetry tracing behind clientOptions and env var --- core/packages/gax/src/createApiCall.ts | 3 +++ core/packages/gax/test/unit/apiCallable.ts | 24 ++++++++++++++++++++++ core/packages/gax/test/unit/utils.ts | 4 ++++ 3 files changed, 31 insertions(+) diff --git a/core/packages/gax/src/createApiCall.ts b/core/packages/gax/src/createApiCall.ts index 232bf3916f07..5c71300866a0 100644 --- a/core/packages/gax/src/createApiCall.ts +++ b/core/packages/gax/src/createApiCall.ts @@ -18,6 +18,7 @@ * Provides function wrappers that implement page streaming and retrying. */ +import { ClientOptions } from './clientInterface'; import {createAPICaller} from './apiCaller'; import { APICallback, @@ -173,8 +174,10 @@ export function createApiCall( }; if (tracingEnabled) { + console.log('tracing enabled'); return invokeCall; } else { + console.log('tracing disabled'); return invokeCall; } } diff --git a/core/packages/gax/test/unit/apiCallable.ts b/core/packages/gax/test/unit/apiCallable.ts index f341ec12f01f..71a32712445f 100644 --- a/core/packages/gax/test/unit/apiCallable.ts +++ b/core/packages/gax/test/unit/apiCallable.ts @@ -329,6 +329,30 @@ describe('createApiCall', () => { ); } }); + + describe('in regards to OpenTelemetry Tracing', () => { + let consoleSpy: sinon.SinonSpy; + beforeEach(() => { + consoleSpy = sinon.spy(console, 'log'); + }); + + afterEach(() => { + consoleSpy.restore(); + delete process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED; + }); + + it('logs "tracing enabled" when GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED and ClientOptions field is set', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED = 'true'; + const mockClientOptions = { enableTelemetryTracing: true }; + createApiCall(() => { }, { clientOptions: mockClientOptions }); + assert(consoleSpy.calledWith('tracing enabled')); + }); + + it('logs "tracing disabled" when GOOGLE_SDK_NODE_EXPERIMENTAL_O11Y_ENABLED is not set', () => { + createApiCall(() => { }); + assert(consoleSpy.calledWith('tracing disabled')); + }); + }); }); describe('Promise', () => { diff --git a/core/packages/gax/test/unit/utils.ts b/core/packages/gax/test/unit/utils.ts index 81440541a1fb..9b1f80177332 100644 --- a/core/packages/gax/test/unit/utils.ts +++ b/core/packages/gax/test/unit/utils.ts @@ -16,6 +16,7 @@ import {GaxCallPromise} from '../../src/apitypes'; import {createApiCall as realCreateApiCall} from '../../src/createApiCall'; +import {ClientOptions} from '../../src/clientInterface'; import * as gax from '../../src/gax'; import {GoogleError} from '../../src/googleError'; import {Descriptor} from '../../src/descriptor'; @@ -42,6 +43,7 @@ export interface Options { returnCancelFunc?: boolean; cancel?: Function; deadline?: string; + clientOptions?: ClientOptions; } export function createApiCall(func: Function, opts?: Options) { @@ -78,6 +80,8 @@ export function createApiCall(func: Function, opts?: Options) { }), settings, descriptor, + undefined, + opts?.clientOptions, ) as GaxCallPromise; } From aa96cebf40289c72a1d63360bb738f582e7caf71 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Mon, 3 Aug 2026 18:47:52 -0700 Subject: [PATCH 8/9] feat(gax): add TracerHelper for observability trace contexts --- .../gax/src/observability/TracerHelper.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 core/packages/gax/src/observability/TracerHelper.ts diff --git a/core/packages/gax/src/observability/TracerHelper.ts b/core/packages/gax/src/observability/TracerHelper.ts new file mode 100644 index 000000000000..8dce457e2b6e --- /dev/null +++ b/core/packages/gax/src/observability/TracerHelper.ts @@ -0,0 +1,21 @@ +import { context, trace, Tracer } from '@opentelemetry/api'; +import * as grpc from '@grpc/grpc-js'; + +export interface StaticTraceContext { + gcpClientService?: string; + gcpVersion?: string; + gcpRepo?: string; + gcpArtifact?: string; +} + +export interface DynamicTraceContext { + clientName: string; + methodName: string; + rpcType: 'grpc' | 'http'; +} + +export function getGaxTracer(): Tracer { + return trace.getTracer('google-gax'); +} + +export async function traceAttempt() { } \ No newline at end of file From 2bd856a7f8853c63cbdcc9788276ad3b00824e6e Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Mon, 3 Aug 2026 18:48:24 -0700 Subject: [PATCH 9/9] update TracerHelper.ts with user changes --- core/packages/gax/src/observability/TracerHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/packages/gax/src/observability/TracerHelper.ts b/core/packages/gax/src/observability/TracerHelper.ts index 8dce457e2b6e..9e6173317623 100644 --- a/core/packages/gax/src/observability/TracerHelper.ts +++ b/core/packages/gax/src/observability/TracerHelper.ts @@ -1,4 +1,4 @@ -import { context, trace, Tracer } from '@opentelemetry/api'; +import { context, trace, Tracer } from `@opentelemetry/api`; import * as grpc from '@grpc/grpc-js'; export interface StaticTraceContext {