@@ -17,9 +17,17 @@ import {Gaxios, GaxiosOptions, GaxiosPromise, GaxiosResponse} from 'gaxios';
17
17
18
18
import { Credentials } from './credentials' ;
19
19
import { OriginalAndCamel , originalOrCamelOptions } from '../util' ;
20
+ import { log as makeLog } from 'google-logging-utils' ;
20
21
21
22
import { PRODUCT_NAME , USER_AGENT } from '../shared.cjs' ;
22
23
24
+ /**
25
+ * Easy access to symbol-indexed strings on config objects.
26
+ */
27
+ export type SymbolIndexString = {
28
+ [ key : symbol ] : string | undefined ;
29
+ } ;
30
+
23
31
/**
24
32
* Base auth configurations (e.g. from JWT or `.json` files) with conventional
25
33
* camelCased options.
@@ -210,6 +218,16 @@ export abstract class AuthClient
210
218
forceRefreshOnFailure = false ;
211
219
universeDomain = DEFAULT_UNIVERSE ;
212
220
221
+ /**
222
+ * Symbols that can be added to GaxiosOptions to specify the method name that is
223
+ * making an RPC call, for logging purposes, as well as a string ID that can be
224
+ * used to correlate calls and responses.
225
+ */
226
+ static readonly RequestMethodNameSymbol : unique symbol = Symbol (
227
+ 'request method name' ,
228
+ ) ;
229
+ static readonly RequestLogIdSymbol : unique symbol = Symbol ( 'request log id' ) ;
230
+
213
231
constructor ( opts : AuthClientOptions = { } ) {
214
232
super ( ) ;
215
233
@@ -229,6 +247,9 @@ export abstract class AuthClient
229
247
this . transporter . interceptors . request . add (
230
248
AuthClient . DEFAULT_REQUEST_INTERCEPTOR ,
231
249
) ;
250
+ this . transporter . interceptors . response . add (
251
+ AuthClient . DEFAULT_RESPONSE_INTERCEPTOR ,
252
+ ) ;
232
253
}
233
254
234
255
if ( opts . eagerRefreshThresholdMillis ) {
@@ -322,6 +343,7 @@ export abstract class AuthClient
322
343
return target ;
323
344
}
324
345
346
+ static log = makeLog ( 'auth' ) ;
325
347
static readonly DEFAULT_REQUEST_INTERCEPTOR : Parameters <
326
348
Gaxios [ 'interceptors' ] [ 'request' ] [ 'add' ]
327
349
> [ 0 ] = {
@@ -340,10 +362,104 @@ export abstract class AuthClient
340
362
config . headers . set ( 'User-Agent' , `${ userAgent } ${ USER_AGENT } ` ) ;
341
363
}
342
364
365
+ try {
366
+ const symbols : SymbolIndexString =
367
+ config as unknown as SymbolIndexString ;
368
+ const methodName = symbols [ AuthClient . RequestMethodNameSymbol ] ;
369
+
370
+ // This doesn't need to be very unique or interesting, it's just an aid for
371
+ // matching requests to responses.
372
+ const logId = `${ Math . floor ( Math . random ( ) * 1000 ) } ` ;
373
+ symbols [ AuthClient . RequestLogIdSymbol ] = logId ;
374
+
375
+ // Boil down the object we're printing out.
376
+ const logObject = {
377
+ url : config . url ,
378
+ headers : config . headers ,
379
+ } ;
380
+ if ( methodName ) {
381
+ AuthClient . log . info (
382
+ '%s [%s] request %j' ,
383
+ methodName ,
384
+ logId ,
385
+ logObject ,
386
+ ) ;
387
+ } else {
388
+ AuthClient . log . info ( '[%s] request %j' , logId , logObject ) ;
389
+ }
390
+ } catch ( e ) {
391
+ // Logging must not create new errors; swallow them all.
392
+ }
393
+
343
394
return config ;
344
395
} ,
345
396
} ;
346
397
398
+ static readonly DEFAULT_RESPONSE_INTERCEPTOR : Parameters <
399
+ Gaxios [ 'interceptors' ] [ 'response' ] [ 'add' ]
400
+ > [ 0 ] = {
401
+ resolved : async response => {
402
+ try {
403
+ const symbols : SymbolIndexString =
404
+ response . config as unknown as SymbolIndexString ;
405
+ const methodName = symbols [ AuthClient . RequestMethodNameSymbol ] ;
406
+ const logId = symbols [ AuthClient . RequestLogIdSymbol ] ;
407
+ if ( methodName ) {
408
+ AuthClient . log . info (
409
+ '%s [%s] response %j' ,
410
+ methodName ,
411
+ logId ,
412
+ response . data ,
413
+ ) ;
414
+ } else {
415
+ AuthClient . log . info ( '[%s] response %j' , logId , response . data ) ;
416
+ }
417
+ } catch ( e ) {
418
+ // Logging must not create new errors; swallow them all.
419
+ }
420
+
421
+ return response ;
422
+ } ,
423
+ rejected : async error => {
424
+ try {
425
+ const symbols : SymbolIndexString =
426
+ error . config as unknown as SymbolIndexString ;
427
+ const methodName = symbols [ AuthClient . RequestMethodNameSymbol ] ;
428
+ const logId = symbols [ AuthClient . RequestLogIdSymbol ] ;
429
+ if ( methodName ) {
430
+ AuthClient . log . info (
431
+ '%s [%s] error %j' ,
432
+ methodName ,
433
+ logId ,
434
+ error . response ?. data ,
435
+ ) ;
436
+ } else {
437
+ AuthClient . log . error ( '[%s] error %j' , logId , error . response ?. data ) ;
438
+ }
439
+ } catch ( e ) {
440
+ // Logging must not create new errors; swallow them all.
441
+ }
442
+
443
+ // Re-throw the error.
444
+ throw error ;
445
+ } ,
446
+ } ;
447
+
448
+ /**
449
+ * Sets the method name that is making a Gaxios request, so that logging may tag
450
+ * log lines with the operation.
451
+ * @param config A Gaxios request config
452
+ * @param methodName The method name making the call
453
+ */
454
+ static setMethodName ( config : GaxiosOptions , methodName : string ) {
455
+ try {
456
+ const symbols : SymbolIndexString = config as unknown as SymbolIndexString ;
457
+ symbols [ AuthClient . RequestMethodNameSymbol ] = methodName ;
458
+ } catch ( e ) {
459
+ // Logging must not create new errors; swallow them all.
460
+ }
461
+ }
462
+
347
463
/**
348
464
* Retry config for Auth-related requests.
349
465
*
0 commit comments