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..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 @@ -197,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 e505db0eef11..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 @@ -203,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 %}; 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 diff --git a/core/packages/gax/src/createApiCall.ts b/core/packages/gax/src/createApiCall.ts index b3fdee987a6d..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, @@ -27,6 +28,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 +61,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,7 +70,12 @@ export function createApiCall( // the following apiCaller will be used for all calls of this function... const apiCaller = createAPICaller(settings, descriptor); - 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, @@ -164,4 +172,12 @@ export function createApiCall( // or to cancel the ongoing call. return currentApiCaller.result(ongoingCall); }; + + if (tracingEnabled) { + console.log('tracing enabled'); + return invokeCall; + } else { + console.log('tracing disabled'); + return invokeCall; + } } diff --git a/core/packages/gax/src/observability/TracerHelper.ts b/core/packages/gax/src/observability/TracerHelper.ts new file mode 100644 index 000000000000..9e6173317623 --- /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 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; }