From 2900dafba110dd21e751696fe10cefaecbe51451 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 16 Oct 2026 14:37:49 -0700 Subject: [PATCH 1/4] feat(gax): rename checkTelemetryEnabled to checkTracingEnabled and add checkLoggingEnabled --- core/packages/gax/src/createApiCall.ts | 4 +- core/packages/gax/src/gax.ts | 12 +- core/packages/gax/src/index.ts | 2 +- core/packages/gax/src/observability/util.ts | 38 ++++- core/packages/gax/test/unit/exports.ts | 4 +- core/packages/gax/test/unit/util.ts | 151 +++++++++++++++++--- 6 files changed, 185 insertions(+), 26 deletions(-) diff --git a/core/packages/gax/src/createApiCall.ts b/core/packages/gax/src/createApiCall.ts index 40b271a441c4..edb3e8482873 100644 --- a/core/packages/gax/src/createApiCall.ts +++ b/core/packages/gax/src/createApiCall.ts @@ -41,7 +41,7 @@ import { traceAttempt, ResendRecorder, } from './observability/TracerHelper'; -import {checkTelemetryEnabled} from './observability/util'; +import {checkTracingEnabled} from './observability/util'; import { StaticTraceContext, DynamicTraceContext, @@ -83,7 +83,7 @@ export function createApiCall( // the following apiCaller will be used for all calls of this function... const apiCaller = createAPICaller(settings, descriptor); - const tracingEnabled = checkTelemetryEnabled(settings); + const tracingEnabled = checkTracingEnabled(settings); const staticArgs: StaticTraceContext | undefined = tracingEnabled ? resolveStaticTraceContext(settings) : undefined; diff --git a/core/packages/gax/src/gax.ts b/core/packages/gax/src/gax.ts index 98420f82179b..b013471b78f1 100644 --- a/core/packages/gax/src/gax.ts +++ b/core/packages/gax/src/gax.ts @@ -24,7 +24,7 @@ import {warn} from './warnings'; import {GoogleError} from './googleError'; import {BundleOptions} from './bundlingCalls/bundleExecutor'; import {toLowerCamelCase} from './util'; -import {checkTelemetryEnabled} from './observability/util'; +import {checkTracingEnabled} from './observability/util'; import {Status} from './status'; import {RequestType} from './apitypes'; @@ -173,6 +173,7 @@ export interface CallOptions { apiName?: string; retryRequestOptions?: RetryRequestOptions; enableTelemetryTracing?: boolean; + enableTelemetryLogging?: boolean; /** * Internal context used by resumable upload methods. Populated by * GAPIC-generated client libraries; do not set manually. @@ -195,6 +196,7 @@ export class CallSettings { apiName?: string; retryRequestOptions?: RetryRequestOptions; enableTelemetryTracing?: boolean; + enableTelemetryLogging?: boolean; resumableUpload?: ResumableUploadContext; /** @@ -230,6 +232,7 @@ export class CallSettings { this.apiName = settings.apiName ?? undefined; this.retryRequestOptions = settings.retryRequestOptions; this.enableTelemetryTracing = settings.enableTelemetryTracing; + this.enableTelemetryLogging = settings.enableTelemetryLogging; this.resumableUpload = 'resumableUpload' in settings ? settings.resumableUpload : undefined; } @@ -256,6 +259,7 @@ export class CallSettings { let apiName = this.apiName; let retryRequestOptions = this.retryRequestOptions; let enableTelemetryTracing = this.enableTelemetryTracing; + let enableTelemetryLogging = this.enableTelemetryLogging; let resumableUpload = this.resumableUpload; // If the user provides a timeout to the method, that timeout value will be used @@ -315,6 +319,9 @@ export class CallSettings { if ('enableTelemetryTracing' in options) { enableTelemetryTracing = options.enableTelemetryTracing; } + if ('enableTelemetryLogging' in options) { + enableTelemetryLogging = options.enableTelemetryLogging; + } if ('resumableUpload' in options) { resumableUpload = options.resumableUpload; } @@ -331,6 +338,7 @@ export class CallSettings { apiName, retryRequestOptions, enableTelemetryTracing, + enableTelemetryLogging, resumableUpload, }); } @@ -827,7 +835,7 @@ export function constructSettings( const envTracing = env.GOOGLE_SDK_NODE_ENABLE_TRACING?.trim(); const isEnvSet = envTracing !== undefined && envTracing !== ''; - const isEnvEnabled = checkTelemetryEnabled(); + const isEnvEnabled = checkTracingEnabled(); // Tracing is only enabled via GOOGLE_SDK_NODE_ENABLE_TRACING or when the user // passes enableTelemetryTracing: true in their client options. If diff --git a/core/packages/gax/src/index.ts b/core/packages/gax/src/index.ts index 89d95d5cab8a..c08b855502a9 100644 --- a/core/packages/gax/src/index.ts +++ b/core/packages/gax/src/index.ts @@ -128,7 +128,7 @@ export { export {makeUUID, decodeAnyProtosInArray, decodeProtobufAny} from './util'; -export {checkTelemetryEnabled} from './observability/util'; +export {checkTracingEnabled} from './observability/util'; export { StaticTraceContext, resolveStaticTraceContext, diff --git a/core/packages/gax/src/observability/util.ts b/core/packages/gax/src/observability/util.ts index c1d3441944e7..667148b945ee 100644 --- a/core/packages/gax/src/observability/util.ts +++ b/core/packages/gax/src/observability/util.ts @@ -27,7 +27,7 @@ import {CallSettings} from '../gax'; * @param settings * @returns true if telemetry tracing is enabled, false otherwise */ -export function checkTelemetryEnabled(settings?: CallSettings): boolean { +export function checkTracingEnabled(settings?: CallSettings): boolean { // `process` is undeclared in browsers and some edge runtimes, where reading // it would throw a ReferenceError rather than yield undefined, so it is // reached through a `typeof` guard and stands in as an empty environment. @@ -47,3 +47,39 @@ export function checkTelemetryEnabled(settings?: CallSettings): boolean { settings?.otherArgs?.enableTelemetryTracing, ); } + +/** + * Checks if telemetry logging is enabled. + * + * Logging is only enabled when `GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING` is enabled + * AND either the `GOOGLE_SDK_NODE_ENABLE_LOGGING` environment variable is enabled + * or a user passes `enableTelemetryLogging: true` in their client options. + * If `GOOGLE_SDK_NODE_ENABLE_LOGGING` is explicitly set, it takes precedence over + * the client option. + * + * @param settings + * @returns true if telemetry logging is enabled, false otherwise + */ +export function checkLoggingEnabled(settings?: CallSettings): boolean { + const env: Record = + typeof process === 'object' && typeof process.env === 'object' + ? process.env + : {}; + const experimentalOptIn = + env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING?.trim().toLowerCase(); + if (experimentalOptIn !== 'true' && experimentalOptIn !== '1') { + return false; + } + + const envOptIn = env.GOOGLE_SDK_NODE_ENABLE_LOGGING?.trim(); + + if (envOptIn !== undefined && envOptIn !== '') { + const lower = envOptIn.toLowerCase(); + return lower === 'true' || lower === '1'; + } + + return Boolean( + settings?.enableTelemetryLogging || + settings?.otherArgs?.enableTelemetryLogging, + ); +} diff --git a/core/packages/gax/test/unit/exports.ts b/core/packages/gax/test/unit/exports.ts index 4c74393b3adc..ce28d16d1f26 100644 --- a/core/packages/gax/test/unit/exports.ts +++ b/core/packages/gax/test/unit/exports.ts @@ -62,8 +62,8 @@ describe('exports', () => { it('exports protobufMinimal', () => { assert(typeof index.protobufMinimal === 'object'); }); - it('exports checkTelemetryEnabled', () => { - assert(typeof index.checkTelemetryEnabled === 'function'); + it('exports checkTracingEnabled', () => { + assert(typeof index.checkTracingEnabled === 'function'); }); it('exports resolveStaticTraceContext', () => { assert(typeof index.resolveStaticTraceContext === 'function'); diff --git a/core/packages/gax/test/unit/util.ts b/core/packages/gax/test/unit/util.ts index aaa4072bcaa0..eaf493b275ea 100644 --- a/core/packages/gax/test/unit/util.ts +++ b/core/packages/gax/test/unit/util.ts @@ -26,7 +26,10 @@ import { decodeAnyProtosInArray, ignoredClientHeaderTokens, } from '../../src/util'; -import {checkTelemetryEnabled} from '../../src/observability/util'; +import { + checkTracingEnabled, + checkLoggingEnabled, +} from '../../src/observability/util'; import {CallSettings} from '../../src/gax'; import * as protobuf from 'protobufjs'; import protosJson from '../../protos/status.json'; @@ -200,7 +203,7 @@ describe('util.ts', () => { ); }); - describe('checkTelemetryEnabled', () => { + describe('checkTracingEnabled', () => { afterEach(() => { delete process.env.GOOGLE_SDK_NODE_ENABLE_TRACING; }); @@ -210,48 +213,48 @@ describe('util.ts', () => { }); it('returns true when settings are configured with enableTelemetryTracing', () => { - assert.strictEqual(checkTelemetryEnabled(mockSettings), true); + assert.strictEqual(checkTracingEnabled(mockSettings), true); }); it('returns false when enableTelemetryTracing is false on settings', () => { const disabledSettings = new CallSettings({ enableTelemetryTracing: false, }); - assert.strictEqual(checkTelemetryEnabled(disabledSettings), false); + assert.strictEqual(checkTracingEnabled(disabledSettings), false); }); it('returns false when enableTelemetryTracing is not set on settings', () => { const noTracingSettings = new CallSettings({}); - assert.strictEqual(checkTelemetryEnabled(noTracingSettings), false); + assert.strictEqual(checkTracingEnabled(noTracingSettings), false); }); it('returns false when settings is undefined', () => { - assert.strictEqual(checkTelemetryEnabled(undefined), false); + assert.strictEqual(checkTracingEnabled(undefined), false); }); it('returns true when GOOGLE_SDK_NODE_ENABLE_TRACING=true and the client option is not set', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = 'true'; - assert.strictEqual(checkTelemetryEnabled(new CallSettings({})), true); + assert.strictEqual(checkTracingEnabled(new CallSettings({})), true); }); it('returns true when GOOGLE_SDK_NODE_ENABLE_TRACING=true even if settings is undefined', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = 'true'; - assert.strictEqual(checkTelemetryEnabled(), true); + assert.strictEqual(checkTracingEnabled(), true); }); it('accepts GOOGLE_SDK_NODE_ENABLE_TRACING case-insensitively', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = 'TRUE'; - assert.strictEqual(checkTelemetryEnabled(new CallSettings({})), true); + assert.strictEqual(checkTracingEnabled(new CallSettings({})), true); }); it('returns false when GOOGLE_SDK_NODE_ENABLE_TRACING=false overrides the client option', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = 'false'; - assert.strictEqual(checkTelemetryEnabled(mockSettings), false); + assert.strictEqual(checkTracingEnabled(mockSettings), false); }); it('returns false when GOOGLE_SDK_NODE_ENABLE_TRACING=0 overrides the client option', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = '0'; - assert.strictEqual(checkTelemetryEnabled(mockSettings), false); + assert.strictEqual(checkTracingEnabled(mockSettings), false); }); it('returns true when GOOGLE_SDK_NODE_ENABLE_TRACING=true overrides enableTelemetryTracing=false on client settings', () => { @@ -259,7 +262,7 @@ describe('util.ts', () => { const disabledSettings = new CallSettings({ enableTelemetryTracing: false, }); - assert.strictEqual(checkTelemetryEnabled(disabledSettings), true); + assert.strictEqual(checkTracingEnabled(disabledSettings), true); }); it('returns true when GOOGLE_SDK_NODE_ENABLE_TRACING=1 overrides enableTelemetryTracing=false on client settings', () => { @@ -267,23 +270,23 @@ describe('util.ts', () => { const disabledSettings = new CallSettings({ enableTelemetryTracing: false, }); - assert.strictEqual(checkTelemetryEnabled(disabledSettings), true); + assert.strictEqual(checkTracingEnabled(disabledSettings), true); }); it('falls back to the client option when GOOGLE_SDK_NODE_ENABLE_TRACING is empty or whitespace', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = ''; - assert.strictEqual(checkTelemetryEnabled(mockSettings), true); + assert.strictEqual(checkTracingEnabled(mockSettings), true); process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = ' '; - assert.strictEqual(checkTelemetryEnabled(mockSettings), true); + assert.strictEqual(checkTracingEnabled(mockSettings), true); }); it('treats an unrecognized GOOGLE_SDK_NODE_ENABLE_TRACING value as disabled', () => { process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = 'yes'; - assert.strictEqual(checkTelemetryEnabled(mockSettings), false); + assert.strictEqual(checkTracingEnabled(mockSettings), false); process.env.GOOGLE_SDK_NODE_ENABLE_TRACING = 'foo'; - assert.strictEqual(checkTelemetryEnabled(mockSettings), false); + assert.strictEqual(checkTracingEnabled(mockSettings), false); }); it('does not throw when process is undefined, as in a browser', () => { @@ -295,7 +298,7 @@ describe('util.ts', () => { let thrown: unknown; delete globals.process; try { - result = checkTelemetryEnabled(mockSettings); + result = checkTracingEnabled(mockSettings); } catch (e) { thrown = e; } finally { @@ -306,6 +309,118 @@ describe('util.ts', () => { }); }); + describe('checkLoggingEnabled', () => { + afterEach(() => { + delete process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING; + delete process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING; + }); + + const mockSettings = new CallSettings({ + enableTelemetryLogging: true, + }); + + it('returns false when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING is not set even if logging is enabled', () => { + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'true'; + assert.strictEqual(checkLoggingEnabled(mockSettings), false); + }); + + it('returns false when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING is false', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'false'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'true'; + assert.strictEqual(checkLoggingEnabled(mockSettings), false); + }); + + it('returns true when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=true and settings have enableTelemetryLogging', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + assert.strictEqual(checkLoggingEnabled(mockSettings), true); + }); + + it('returns true when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=1 and settings.otherArgs have enableTelemetryLogging', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = '1'; + const otherArgsSettings = new CallSettings({ + otherArgs: {enableTelemetryLogging: true}, + }); + assert.strictEqual(checkLoggingEnabled(otherArgsSettings), true); + }); + + it('returns false when enableTelemetryLogging is false or not set on settings and env var is not set', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + const disabledSettings = new CallSettings({ + enableTelemetryLogging: false, + }); + assert.strictEqual(checkLoggingEnabled(disabledSettings), false); + assert.strictEqual(checkLoggingEnabled(new CallSettings({})), false); + assert.strictEqual(checkLoggingEnabled(undefined), false); + }); + + it('returns true when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=true and GOOGLE_SDK_NODE_ENABLE_LOGGING=true', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'true'; + assert.strictEqual(checkLoggingEnabled(new CallSettings({})), true); + assert.strictEqual(checkLoggingEnabled(), true); + }); + + it('accepts GOOGLE_SDK_NODE_ENABLE_LOGGING and GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING case-insensitively', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'TRUE'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'TRUE'; + assert.strictEqual(checkLoggingEnabled(new CallSettings({})), true); + }); + + it('returns false when GOOGLE_SDK_NODE_ENABLE_LOGGING=false or 0 overrides the client option', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'false'; + assert.strictEqual(checkLoggingEnabled(mockSettings), false); + + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = '0'; + assert.strictEqual(checkLoggingEnabled(mockSettings), false); + }); + + it('returns true when GOOGLE_SDK_NODE_ENABLE_LOGGING=true or 1 overrides enableTelemetryLogging=false on settings', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + const disabledSettings = new CallSettings({ + enableTelemetryLogging: false, + }); + + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'true'; + assert.strictEqual(checkLoggingEnabled(disabledSettings), true); + + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = '1'; + assert.strictEqual(checkLoggingEnabled(disabledSettings), true); + }); + + it('falls back to the client option when GOOGLE_SDK_NODE_ENABLE_LOGGING is empty or whitespace', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = ''; + assert.strictEqual(checkLoggingEnabled(mockSettings), true); + + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = ' '; + assert.strictEqual(checkLoggingEnabled(mockSettings), true); + }); + + it('treats an unrecognized GOOGLE_SDK_NODE_ENABLE_LOGGING value as disabled', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'yes'; + assert.strictEqual(checkLoggingEnabled(mockSettings), false); + }); + + it('does not throw when process is undefined, as in a browser', () => { + const globals = globalThis as {process?: NodeJS.Process}; + const originalProcess = globals.process; + let result: boolean | undefined; + let thrown: unknown; + delete globals.process; + try { + result = checkLoggingEnabled(mockSettings); + } catch (e) { + thrown = e; + } finally { + globals.process = originalProcess; + } + assert.strictEqual(thrown, undefined); + assert.strictEqual(result, false); + }); + }); + describe('ignoredClientHeaderTokens', () => { it('defines ignoredClientHeaderTokens containing ignored client header tokens', () => { assert(Array.isArray(ignoredClientHeaderTokens)); From 6ca3ebcee72eda15a4794f433e7ffc443a55e387 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 16 Oct 2026 14:53:51 -0700 Subject: [PATCH 2/4] feat(gax): add enableTelemetryLogging to ClientOptions, GrpcClient, and constructSettings --- core/packages/gax/src/clientInterface.ts | 10 +++ core/packages/gax/src/fallback.ts | 9 ++ core/packages/gax/src/gax.ts | 32 ++++++- core/packages/gax/src/grpc.ts | 15 ++++ core/packages/gax/test/unit/gax.ts | 90 +++++++++++++++++++- core/packages/gax/test/unit/grpc-fallback.ts | 33 +++++++ core/packages/gax/test/unit/grpc.ts | 22 +++++ 7 files changed, 203 insertions(+), 8 deletions(-) diff --git a/core/packages/gax/src/clientInterface.ts b/core/packages/gax/src/clientInterface.ts index a7c93ac32e53..c21fab80c02b 100644 --- a/core/packages/gax/src/clientInterface.ts +++ b/core/packages/gax/src/clientInterface.ts @@ -50,6 +50,16 @@ export interface ClientOptions * this option whenever it is set. */ enableTelemetryTracing?: boolean; + /** + * Whether to enable telemetry logging for the client. + * + * Logging is only enabled when `GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING` is enabled + * AND either the `GOOGLE_SDK_NODE_ENABLE_LOGGING` environment variable is enabled + * or a user passes `enableTelemetryLogging: true` in their client options. The + * `GOOGLE_SDK_NODE_ENABLE_LOGGING` environment variable overrides this option + * whenever it is set. + */ + enableTelemetryLogging?: boolean; } export interface Descriptors { diff --git a/core/packages/gax/src/fallback.ts b/core/packages/gax/src/fallback.ts index 4cd7aae4415b..4f3266e1a95c 100644 --- a/core/packages/gax/src/fallback.ts +++ b/core/packages/gax/src/fallback.ts @@ -110,6 +110,7 @@ export class GrpcClient { private _servicePath?: string; private _port?: number; private _enableTelemetryTracing?: boolean; + private _enableTelemetryLogging?: boolean; /** * In rare cases users might need to deallocate all memory consumed by loaded protos. @@ -166,6 +167,9 @@ export class GrpcClient { this._enableTelemetryTracing = ( options as GrpcClientOptions ).enableTelemetryTracing; + this._enableTelemetryLogging = ( + options as GrpcClientOptions + ).enableTelemetryLogging; } /** @@ -221,6 +225,8 @@ export class GrpcClient { * @param {Object} headers - A dictionary of additional HTTP header name to * its value. * @param {boolean} [enableTelemetryTracing] - Flag to enable telemetry tracing. + * @param {unknown} [_legacyTelemetryInfo] - Unused legacy parameter kept for backward compatibility. + * @param {boolean} [enableTelemetryLogging] - Flag to enable telemetry logging. * @return {Object} A mapping of method names to CallSettings. */ constructSettings( @@ -231,6 +237,7 @@ export class GrpcClient { enableTelemetryTracing?: boolean, // eslint-disable-next-line @typescript-eslint/no-unused-vars _legacyTelemetryInfo?: unknown, + enableTelemetryLogging?: boolean, ) { function buildMetadata(abTests: {}, moreHeaders: OutgoingHttpHeaders) { const metadata: OutgoingHttpHeaders = {}; @@ -305,6 +312,8 @@ export class GrpcClient { Status, otherArgs, enableTelemetryTracing ?? this._enableTelemetryTracing, + _legacyTelemetryInfo, + enableTelemetryLogging ?? this._enableTelemetryLogging, ); } diff --git a/core/packages/gax/src/gax.ts b/core/packages/gax/src/gax.ts index b013471b78f1..7405a87064dd 100644 --- a/core/packages/gax/src/gax.ts +++ b/core/packages/gax/src/gax.ts @@ -24,7 +24,7 @@ import {warn} from './warnings'; import {GoogleError} from './googleError'; import {BundleOptions} from './bundlingCalls/bundleExecutor'; import {toLowerCamelCase} from './util'; -import {checkTracingEnabled} from './observability/util'; +import {checkTracingEnabled, checkLoggingEnabled} from './observability/util'; import {Status} from './status'; import {RequestType} from './apitypes'; @@ -815,6 +815,9 @@ export interface ClientConfig { * calls. * @param {boolean} [enableTelemetryTracing] - Flag to enable telemetry tracing * when passed in client options (or overridden by `GOOGLE_SDK_NODE_ENABLE_TRACING`). + * @param {unknown} [_legacyTelemetryInfo] - Unused legacy parameter kept for backward compatibility. + * @param {boolean} [enableTelemetryLogging] - Flag to enable telemetry logging + * when passed in client options (or overridden by `GOOGLE_SDK_NODE_ENABLE_LOGGING`). * @return {Object} A mapping from method name to CallSettings, or null if the * service is not found in the config. */ @@ -827,6 +830,7 @@ export function constructSettings( enableTelemetryTracing?: boolean, // eslint-disable-next-line @typescript-eslint/no-unused-vars _legacyTelemetryInfo?: unknown, + enableTelemetryLogging?: boolean, ) { const env: Record = typeof process === 'object' && typeof process.env === 'object' @@ -844,6 +848,18 @@ export function constructSettings( ? isEnvEnabled : Boolean(enableTelemetryTracing); + const experimentalLogging = + env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING?.trim().toLowerCase(); + const isExperimentalLoggingEnabled = + experimentalLogging === 'true' || experimentalLogging === '1'; + const envLogging = env.GOOGLE_SDK_NODE_ENABLE_LOGGING?.trim(); + const isLoggingEnvSet = envLogging !== undefined && envLogging !== ''; + const isLoggingEnvEnabled = checkLoggingEnabled(); + + const loggingRequested = + isExperimentalLoggingEnabled && + (isLoggingEnvSet ? isLoggingEnvEnabled : Boolean(enableTelemetryLogging)); + otherArgs = otherArgs || {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any const defaults: any = {}; @@ -901,11 +917,19 @@ export function constructSettings( bundleOptions: bundlingConfig ? createBundleOptions(bundlingConfig) : null, - otherArgs: tracingRequested - ? {...otherArgs, internalMethodName: methodName} - : otherArgs, + otherArgs: + tracingRequested || loggingRequested + ? {...otherArgs, internalMethodName: methodName} + : otherArgs, apiName, enableTelemetryTracing: isEnvSet ? isEnvEnabled : enableTelemetryTracing, + enableTelemetryLogging: isExperimentalLoggingEnabled + ? isLoggingEnvSet + ? isLoggingEnvEnabled + : enableTelemetryLogging + : enableTelemetryLogging !== undefined || isLoggingEnvSet + ? false + : undefined, }); } diff --git a/core/packages/gax/src/grpc.ts b/core/packages/gax/src/grpc.ts index 37b449b60acc..00d5603495fd 100644 --- a/core/packages/gax/src/grpc.ts +++ b/core/packages/gax/src/grpc.ts @@ -63,6 +63,14 @@ export interface GrpcClientOptions extends GoogleAuthOptions { * options. */ enableTelemetryTracing?: boolean; + /** + * Whether to enable telemetry logging for the client. + * + * Logging is only enabled when `GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING` is enabled + * AND either the `GOOGLE_SDK_NODE_ENABLE_LOGGING` environment variable is enabled + * or a user passes `enableTelemetryLogging: true` in their client options. + */ + enableTelemetryLogging?: boolean; } export interface MetadataValue { @@ -134,6 +142,7 @@ export class GrpcClient { private _servicePath?: string; private _port?: number; private _enableTelemetryTracing?: boolean; + private _enableTelemetryLogging?: boolean; /** * Base directory for resolving client certificates. * @@ -193,6 +202,7 @@ export class GrpcClient { ? parseInt(options.port, 10) : options.port; this._enableTelemetryTracing = options.enableTelemetryTracing; + this._enableTelemetryLogging = options.enableTelemetryLogging; const minimumVersion = 10; const major = Number(process.version.match(/^v(\d+)/)?.[1]); @@ -396,6 +406,8 @@ export class GrpcClient { * @param {Object} headers - A dictionary of additional HTTP header name to * its value. * @param {boolean} [enableTelemetryTracing] - Flag to enable telemetry tracing. + * @param {unknown} [_legacyTelemetryInfo] - Unused legacy parameter kept for backward compatibility. + * @param {boolean} [enableTelemetryLogging] - Flag to enable telemetry logging. * @return {Object} A mapping of method names to CallSettings. */ constructSettings( @@ -406,6 +418,7 @@ export class GrpcClient { enableTelemetryTracing?: boolean, // eslint-disable-next-line @typescript-eslint/no-unused-vars _legacyTelemetryInfo?: unknown, + enableTelemetryLogging?: boolean, ) { const otherArgs: Record = { metadataBuilder: this.metadataBuilder(headers), @@ -423,6 +436,8 @@ export class GrpcClient { this.grpc.status, otherArgs, enableTelemetryTracing ?? this._enableTelemetryTracing, + _legacyTelemetryInfo, + enableTelemetryLogging ?? this._enableTelemetryLogging, ); } diff --git a/core/packages/gax/test/unit/gax.ts b/core/packages/gax/test/unit/gax.ts index eba2713b732d..18ce99020234 100644 --- a/core/packages/gax/test/unit/gax.ts +++ b/core/packages/gax/test/unit/gax.ts @@ -101,6 +101,8 @@ function expectBackoffSettings(obj: gax.BackoffSettings) { describe('gax construct settings', () => { afterEach(() => { delete process.env.GOOGLE_SDK_NODE_ENABLE_TRACING; + delete process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING; + delete process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING; }); it('creates settings', () => { @@ -274,38 +276,118 @@ describe('gax construct settings', () => { assert.strictEqual(settings.otherArgs.internalMethodName, undefined); }); + it('creates settings with enableTelemetryLogging when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=true', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + const otherArgs = {key: 'value'}; + const defaults = gax.constructSettings( + SERVICE_NAME, + A_CONFIG, + {}, + RETRY_DICT, + otherArgs, + undefined, + undefined, + true, + ); + const settings = defaults.bundlingMethod; + assert.strictEqual(settings.enableTelemetryLogging, true); + assert.strictEqual(settings.otherArgs.key, 'value'); + assert.strictEqual(settings.otherArgs.internalMethodName, 'BundlingMethod'); + }); + + it('disables logging when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING is not set even if enableTelemetryLogging=true', () => { + const defaults = gax.constructSettings( + SERVICE_NAME, + A_CONFIG, + {}, + RETRY_DICT, + {}, + undefined, + undefined, + true, + ); + const settings = defaults.bundlingMethod; + assert.strictEqual(settings.enableTelemetryLogging, false); + assert.strictEqual(settings.otherArgs.internalMethodName, undefined); + }); + + it('ignores client option when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=true and GOOGLE_SDK_NODE_ENABLE_LOGGING=true', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'true'; + const defaults = gax.constructSettings( + SERVICE_NAME, + A_CONFIG, + {}, + RETRY_DICT, + undefined, + undefined, + undefined, + false, + ); + const settings = defaults.bundlingMethod; + assert.strictEqual(settings.enableTelemetryLogging, true); + assert.strictEqual(settings.otherArgs.internalMethodName, 'BundlingMethod'); + }); + + it('disables logging when GOOGLE_SDK_NODE_ENABLE_LOGGING=false even with client option', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING = 'false'; + const defaults = gax.constructSettings( + SERVICE_NAME, + A_CONFIG, + {}, + RETRY_DICT, + undefined, + undefined, + undefined, + true, + ); + const settings = defaults.bundlingMethod; + assert.strictEqual(settings.enableTelemetryLogging, false); + assert.strictEqual(settings.otherArgs.internalMethodName, undefined); + }); + describe('CallSettings telemetry fields', () => { - it('defaults enableTelemetryTracing to undefined', () => { + it('defaults enableTelemetryTracing and enableTelemetryLogging to undefined', () => { const settings = new gax.CallSettings(); assert.strictEqual(settings.enableTelemetryTracing, undefined); + assert.strictEqual(settings.enableTelemetryLogging, undefined); }); - it('initializes enableTelemetryTracing', () => { + it('initializes enableTelemetryTracing and enableTelemetryLogging', () => { const settings = new gax.CallSettings({ enableTelemetryTracing: true, + enableTelemetryLogging: true, }); assert.strictEqual(settings.enableTelemetryTracing, true); + assert.strictEqual(settings.enableTelemetryLogging, true); }); - it('merges enableTelemetryTracing', () => { + it('merges enableTelemetryTracing and enableTelemetryLogging', () => { const settings = new gax.CallSettings({ enableTelemetryTracing: true, + enableTelemetryLogging: true, }); const merged = settings.merge({ enableTelemetryTracing: false, + enableTelemetryLogging: false, }); assert.strictEqual(merged.enableTelemetryTracing, false); + assert.strictEqual(merged.enableTelemetryLogging, false); }); - it('copies enableTelemetryTracing when merging with null/empty options', () => { + it('copies enableTelemetryTracing and enableTelemetryLogging when merging with null/empty options', () => { const settings = new gax.CallSettings({ enableTelemetryTracing: true, + enableTelemetryLogging: true, }); const mergedNull = settings.merge(null); assert.strictEqual(mergedNull.enableTelemetryTracing, true); + assert.strictEqual(mergedNull.enableTelemetryLogging, true); const mergedEmpty = settings.merge({}); assert.strictEqual(mergedEmpty.enableTelemetryTracing, true); + assert.strictEqual(mergedEmpty.enableTelemetryLogging, true); }); }); }); diff --git a/core/packages/gax/test/unit/grpc-fallback.ts b/core/packages/gax/test/unit/grpc-fallback.ts index ae038d2687a6..b5e470865144 100644 --- a/core/packages/gax/test/unit/grpc-fallback.ts +++ b/core/packages/gax/test/unit/grpc-fallback.ts @@ -327,6 +327,39 @@ describe('grpc-fallback', () => { } }); + it('constructSettings should enable logging when enableTelemetryLogging: true is passed in client options and GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=true', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + try { + const gapicConfig = { + interfaces: { + 'google.showcase.v1beta1.Echo': { + retry_codes: {}, + retry_params: {}, + methods: { + Echo: { + timeout_millis: 60000, + }, + }, + }, + }, + }; + const clientWithLogging = new GrpcClient({ + ...opts, + enableTelemetryLogging: true, + }); + const settings = clientWithLogging.constructSettings( + 'google.showcase.v1beta1.Echo', + gapicConfig, + {}, + {}, + ); + assert.strictEqual(settings.echo.enableTelemetryLogging, true); + assert.strictEqual(settings.echo.otherArgs.internalMethodName, 'Echo'); + } finally { + delete process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING; + } + }); + it('should make a request', async () => { const requestObject = {content: 'test-content'}; const responseType = protos.lookupType('EchoResponse'); diff --git a/core/packages/gax/test/unit/grpc.ts b/core/packages/gax/test/unit/grpc.ts index 0402e499e752..84fabfd6ad00 100644 --- a/core/packages/gax/test/unit/grpc.ts +++ b/core/packages/gax/test/unit/grpc.ts @@ -144,6 +144,8 @@ describe('grpc', () => { afterEach(() => { delete process.env.GOOGLE_SDK_NODE_ENABLE_TRACING; + delete process.env.GOOGLE_SDK_NODE_ENABLE_LOGGING; + delete process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING; }); it('constructs settings without telemetry options', () => { @@ -155,6 +157,7 @@ describe('grpc', () => { ); assert.strictEqual(settings.method.timeout, 10); assert.strictEqual(settings.method.enableTelemetryTracing, undefined); + assert.strictEqual(settings.method.enableTelemetryLogging, undefined); assert.strictEqual( settings.method.otherArgs.internalMethodName, undefined, @@ -195,6 +198,25 @@ describe('grpc', () => { ); }); + it('constructs settings when enableTelemetryLogging: true is passed in client options and GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING=true', () => { + process.env.GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING = 'true'; + const clientWithLogging = gaxGrpc({ + enableTelemetryLogging: true, + }); + const settings = clientWithLogging.constructSettings( + 'google.fake.service', + serviceConfig as unknown as ClientConfig, + {} as unknown as ClientConfig, + {}, + ); + assert.strictEqual(settings.method.timeout, 10); + assert.strictEqual(settings.method.enableTelemetryLogging, true); + assert.strictEqual( + settings.method.otherArgs.internalMethodName, + 'method', + ); + }); + it('accepts legacy 6-argument constructSettings calls while ignoring internalTelemetryInfo', () => { const legacyTelemetryInfo = { gcpClientService: 'fake', From 11c2b9e937386251a2159b9389589447c35d0372 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 16 Oct 2026 15:00:11 -0700 Subject: [PATCH 3/4] feat(gax): set enableTelemetryTracing and enableTelemetryLogging on otherArgs in constructSettings --- core/packages/gax/src/gax.ts | 7 ++++++- core/packages/gax/test/unit/gax.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/packages/gax/src/gax.ts b/core/packages/gax/src/gax.ts index 7405a87064dd..e0c0de22f080 100644 --- a/core/packages/gax/src/gax.ts +++ b/core/packages/gax/src/gax.ts @@ -919,7 +919,12 @@ export function constructSettings( : null, otherArgs: tracingRequested || loggingRequested - ? {...otherArgs, internalMethodName: methodName} + ? { + ...otherArgs, + internalMethodName: methodName, + enableTelemetryTracing: tracingRequested, + enableTelemetryLogging: loggingRequested, + } : otherArgs, apiName, enableTelemetryTracing: isEnvSet ? isEnvEnabled : enableTelemetryTracing, diff --git a/core/packages/gax/test/unit/gax.ts b/core/packages/gax/test/unit/gax.ts index 18ce99020234..148c4f6525cf 100644 --- a/core/packages/gax/test/unit/gax.ts +++ b/core/packages/gax/test/unit/gax.ts @@ -223,6 +223,8 @@ describe('gax construct settings', () => { assert.strictEqual(settings.enableTelemetryTracing, true); assert.strictEqual(settings.otherArgs.key, 'value'); assert.strictEqual(settings.otherArgs.internalMethodName, 'BundlingMethod'); + assert.strictEqual(settings.otherArgs.enableTelemetryTracing, true); + assert.strictEqual(settings.otherArgs.enableTelemetryLogging, false); const pageSettings = defaults.pageStreamingMethod; assert.strictEqual(pageSettings.enableTelemetryTracing, true); @@ -259,6 +261,8 @@ describe('gax construct settings', () => { const settings = defaults.bundlingMethod; assert.strictEqual(settings.enableTelemetryTracing, true); assert.strictEqual(settings.otherArgs.internalMethodName, 'BundlingMethod'); + assert.strictEqual(settings.otherArgs.enableTelemetryTracing, true); + assert.strictEqual(settings.otherArgs.enableTelemetryLogging, false); }); it('disables tracing when GOOGLE_SDK_NODE_ENABLE_TRACING=false even with client option', () => { @@ -293,6 +297,8 @@ describe('gax construct settings', () => { assert.strictEqual(settings.enableTelemetryLogging, true); assert.strictEqual(settings.otherArgs.key, 'value'); assert.strictEqual(settings.otherArgs.internalMethodName, 'BundlingMethod'); + assert.strictEqual(settings.otherArgs.enableTelemetryTracing, false); + assert.strictEqual(settings.otherArgs.enableTelemetryLogging, true); }); it('disables logging when GOOGLE_SDK_NODE_EXPERIMENTAL_LOGGING is not set even if enableTelemetryLogging=true', () => { @@ -327,6 +333,8 @@ describe('gax construct settings', () => { const settings = defaults.bundlingMethod; assert.strictEqual(settings.enableTelemetryLogging, true); assert.strictEqual(settings.otherArgs.internalMethodName, 'BundlingMethod'); + assert.strictEqual(settings.otherArgs.enableTelemetryTracing, false); + assert.strictEqual(settings.otherArgs.enableTelemetryLogging, true); }); it('disables logging when GOOGLE_SDK_NODE_ENABLE_LOGGING=false even with client option', () => { From 36a8e1d73b09f0e6a0d11db96b4c2bc6a43babff Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 16 Oct 2026 15:06:10 -0700 Subject: [PATCH 4/4] style(gax): fix prettier formatting in constructSettings --- core/packages/gax/src/gax.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/packages/gax/src/gax.ts b/core/packages/gax/src/gax.ts index e0c0de22f080..0ba40ea108e3 100644 --- a/core/packages/gax/src/gax.ts +++ b/core/packages/gax/src/gax.ts @@ -920,11 +920,11 @@ export function constructSettings( otherArgs: tracingRequested || loggingRequested ? { - ...otherArgs, - internalMethodName: methodName, - enableTelemetryTracing: tracingRequested, - enableTelemetryLogging: loggingRequested, - } + ...otherArgs, + internalMethodName: methodName, + enableTelemetryTracing: tracingRequested, + enableTelemetryLogging: loggingRequested, + } : otherArgs, apiName, enableTelemetryTracing: isEnvSet ? isEnvEnabled : enableTelemetryTracing,