@@ -30,11 +30,11 @@ import {
3030import {
3131 resolveErrorInfoDomain ,
3232 resolveErrorInfoMetadataAttributes ,
33+ resolveErrorResponsePayload ,
3334 resolveErrorType ,
3435 resolveExceptionMessage ,
3536 resolveExceptionStacktrace ,
3637 resolveExceptionType ,
37- resolveResponsePayload ,
3838 resolveResponseStatusCode ,
3939 resolveRpcStatusName ,
4040} from './errorUtil' ;
@@ -61,19 +61,15 @@ export interface LogPayload {
6161}
6262
6363/**
64- * Resolves a `LogSeverity` value across `google-logging-utils` versions where
65- * `LogSeverity` may be exported as a type only.
64+ * Resolves a `LogSeverity` enum value. Until a `google-logging-utils` release
65+ * containing #9718 is published to npm (this repo uses `link-workspace-packages=false`),
66+ * the published package exports `LogSeverity` as a type only while each enum member's
67+ * runtime value equals its uppercase level string.
6668 */
6769function resolveLogSeverity (
6870 level : 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' ,
6971) : loggingUtils . LogSeverity {
70- return (
71- (
72- loggingUtils as {
73- LogSeverity ?: Record < string , loggingUtils . LogSeverity > ;
74- }
75- ) . LogSeverity ?. [ level ] ?? ( level as loggingUtils . LogSeverity )
76- ) ;
72+ return level as loggingUtils . LogSeverity ;
7773}
7874
7975/**
@@ -153,22 +149,32 @@ export function buildLogAttributes(
153149
154150/**
155151 * Executes `fn` and attaches stream, promise, callback, or synchronous error
156- * handlers to invoke `onError` when the operation fails.
152+ * handlers to invoke `onError` at most once when the operation fails.
157153 */
158154function executeLoggedOperation < T > (
159155 fn : ( loggedCallback ?: APICallback ) => T ,
160156 onError : ( err : unknown ) => void ,
161157 isStreamCall : boolean ,
162158 userCallback ?: APICallback ,
163159) : T {
160+ // Latch to emit at most one log per invocation across execution boundaries.
161+ let hasLogged = false ;
162+ const logOnce = ( err : unknown ) => {
163+ if ( hasLogged ) {
164+ return ;
165+ }
166+ hasLogged = true ;
167+ onError ( err ) ;
168+ } ;
169+
164170 // Wrap user callback to log RPC errors before forwarding.
165171 let callbackInvoked = false ;
166172 const loggedCallback : APICallback | undefined = userCallback
167173 ? function ( this : unknown , ...args : Parameters < APICallback > ) {
168174 callbackInvoked = true ;
169175 const err = args [ 0 ] ;
170176 if ( err ) {
171- onError ( err ) ;
177+ logOnce ( err ) ;
172178 }
173179 userCallback . apply ( this , args ) ;
174180 }
@@ -179,15 +185,15 @@ function executeLoggedOperation<T>(
179185 const result = fn ( loggedCallback ) ;
180186 const promiseTarget = ! isStreamCall ? getPromiseTarget ( result ) : null ;
181187 if ( isStreamCall && result instanceof EventEmitter ) {
182- handleStream ( result , onError , ( ) => { } , Boolean ( loggedCallback ) ) ;
188+ handleStream ( result , logOnce , ( ) => { } , Boolean ( loggedCallback ) ) ;
183189 } else if ( promiseTarget ) {
184- handlePromise ( promiseTarget , onError , ( ) => { } ) ;
190+ handlePromise ( promiseTarget , logOnce , ( ) => { } ) ;
185191 }
186192 return result ;
187193 } catch ( syncErr ) {
188194 // Avoid logging errors thrown inside userCallback after RPC completion.
189195 if ( ! callbackInvoked ) {
190- onError ( syncErr ) ;
196+ logOnce ( syncErr ) ;
191197 }
192198 throw syncErr ;
193199 }
@@ -213,18 +219,10 @@ export function logCall<T = GaxCallResult>(
213219 isStreamCall = false ,
214220 userCallback ?: APICallback ,
215221) : T {
216- // Latch to emit at most one warning log per logical request.
217- let hasLoggedL3 = false ;
218222 // Snapshot initial context in case async rejection loses active context.
219223 const initialContext = context . active ( ) ;
220224
221225 const emitL3WarnLog = ( err : unknown ) => {
222- // Suppress duplicate emissions for the same call.
223- if ( hasLoggedL3 ) {
224- return ;
225- }
226- hasLoggedL3 = true ;
227-
228226 // Resolve logger and active T3 client request span (or outer OpenTelemetry span when GAX tracing is disabled).
229227 const logger = getGaxLogger ( staticArgs ?. gcpClientService ) ;
230228 const activeTrace = getActiveTraceContext ( ) ;
@@ -288,20 +286,22 @@ export function logNetworkAttempt<T = GaxCallResult>(
288286 const initialContext = context . active ( ) ;
289287
290288 const emitL4DebugLog = ( err : unknown ) => {
291- // Resolve logger and active T4 attempt span context for this attempt .
289+ // Resolve logger and active T4 attempt span (or outer OpenTelemetry span when GAX tracing is disabled) .
292290 const logger = getGaxLogger ( staticArgs ?. gcpClientService ) ;
291+ const activeTrace = getActiveTraceContext ( ) ;
292+ const initialTrace = getActiveTraceContext ( initialContext ) ;
293293 const attemptSpan =
294- getActiveTraceContext ( ) ?. attemptSpan ??
295- getActiveTraceContext ( initialContext ) ?. attemptSpan ;
294+ activeTrace ?. attemptSpan ??
295+ initialTrace ?. attemptSpan ??
296+ activeTrace ?. span ??
297+ initialTrace ?. span ;
296298 const spanContext = attemptSpan ?. spanContext ( ) ;
297299 const traceId = spanContext ?. traceId ;
298300 const spanId = spanContext ?. spanId ;
299301
300- // Build structured log attributes and attach attempt metadata payload.
302+ // Build structured log attributes and attach raw attempt response payload when present .
301303 const attributes = buildLogAttributes ( err , attemptDynamicArgs , staticArgs ) ;
302- const responsePayload = resolveResponsePayload (
303- ( err as { metadata ?: unknown } | null | undefined ) ?. metadata ,
304- ) ;
304+ const responsePayload = resolveErrorResponsePayload ( err ) ;
305305 if ( responsePayload !== undefined ) {
306306 attributes [ 'response.payload' ] = responsePayload ;
307307 }
0 commit comments