14
14
* limitations under the License.
15
15
*/
16
16
17
- import { BasePlugin , CanonicalCode , Func , HeaderGetter , HeaderSetter , MessageEventType , RootSpan , Span , SpanKind , TraceOptions } from '@opencensus/core' ;
18
- import * as httpModule from 'http' ;
17
+ import { BasePlugin , CanonicalCode , Func , HeaderGetter , HeaderSetter , MessageEventType , Span , SpanKind , TagMap , TraceOptions } from '@opencensus/core' ;
18
+ import { ClientRequest , ClientResponse , IncomingMessage , request , RequestOptions , ServerResponse } from 'http' ;
19
19
import * as semver from 'semver' ;
20
20
import * as shimmer from 'shimmer' ;
21
21
import * as url from 'url' ;
22
22
import * as uuid from 'uuid' ;
23
- import { HttpPluginConfig , IgnoreMatcher } from './types' ;
23
+ import * as stats from './http-stats' ;
24
+ import { IgnoreMatcher } from './types' ;
24
25
25
- export type HttpGetCallback = ( res : httpModule . IncomingMessage ) => void ;
26
- export type HttpModule = typeof httpModule ;
27
- export type RequestFunction = typeof httpModule . request ;
26
+ export type HttpGetCallback = ( res : IncomingMessage ) => void ;
27
+ export type RequestFunction = typeof request ;
28
+
29
+ function isOpenCensusRequest ( options : RequestOptions ) {
30
+ return options && options . headers &&
31
+ ! ! options . headers [ 'x-opencensus-outgoing-request' ] ;
32
+ }
28
33
29
34
/** Http instrumentation plugin for Opencensus */
30
35
export class HttpPlugin extends BasePlugin {
@@ -47,10 +52,7 @@ export class HttpPlugin extends BasePlugin {
47
52
super ( moduleName ) ;
48
53
}
49
54
50
-
51
- /**
52
- * Patches HTTP incoming and outcoming request functions.
53
- */
55
+ /** Patches HTTP incoming and outcoming request functions. */
54
56
protected applyPatch ( ) {
55
57
this . logger . debug ( 'applying patch to %s@%s' , this . moduleName , this . version ) ;
56
58
@@ -72,9 +74,8 @@ export class HttpPlugin extends BasePlugin {
72
74
// https://nodejs.org/dist/latest/docs/api/http.html#http_http_get_options_callback
73
75
// https://github.com/googleapis/cloud-trace-nodejs/blob/master/src/plugins/plugin-http.ts#L198
74
76
return function getTrace (
75
- options : httpModule . RequestOptions | string ,
76
- callback : HttpGetCallback ) {
77
- const req = httpModule . request ( options , callback ) ;
77
+ options : RequestOptions | string , callback : HttpGetCallback ) {
78
+ const req = request ( options , callback ) ;
78
79
req . end ( ) ;
79
80
return req ;
80
81
} ;
@@ -95,7 +96,6 @@ export class HttpPlugin extends BasePlugin {
95
96
return this . moduleExports ;
96
97
}
97
98
98
-
99
99
/** Unpatches all HTTP patched function. */
100
100
protected applyUnpatch ( ) : void {
101
101
shimmer . unwrap ( this . moduleExports , 'request' ) ;
@@ -149,7 +149,6 @@ export class HttpPlugin extends BasePlugin {
149
149
}
150
150
}
151
151
152
-
153
152
/**
154
153
* Creates spans for incoming requests, restoring spans' context if applied.
155
154
*/
@@ -164,10 +163,11 @@ export class HttpPlugin extends BasePlugin {
164
163
if ( event !== 'request' ) {
165
164
return original . apply ( this , arguments ) ;
166
165
}
167
-
168
- const request : httpModule . IncomingMessage = args [ 0 ] ;
169
- const response : httpModule . ServerResponse = args [ 1 ] ;
166
+ const startTime = Date . now ( ) ;
167
+ const request : IncomingMessage = args [ 0 ] ;
168
+ const response : ServerResponse = args [ 1 ] ;
170
169
const path = request . url ? url . parse ( request . url ) . pathname || '' : '' ;
170
+ const method = request . method || 'GET' ;
171
171
plugin . logger . debug ( '%s plugin incomingRequest' , plugin . moduleName ) ;
172
172
173
173
if ( plugin . isIgnored (
@@ -201,14 +201,15 @@ export class HttpPlugin extends BasePlugin {
201
201
// https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/plugins/plugin-connect.ts#L75)
202
202
const originalEnd = response . end ;
203
203
204
- response . end = function ( this : httpModule . ServerResponse ) {
204
+ response . end = function ( this : ServerResponse ) {
205
205
response . end = originalEnd ;
206
206
const returned = response . end . apply ( this , arguments ) ;
207
207
208
208
const requestUrl = request . url ? url . parse ( request . url ) : null ;
209
209
const host = headers . host || 'localhost' ;
210
210
const userAgent =
211
211
( headers [ 'user-agent' ] || headers [ 'User-Agent' ] ) as string ;
212
+ const tags = new TagMap ( ) ;
212
213
213
214
rootSpan . addAttribute (
214
215
HttpPlugin . ATTRIBUTE_HTTP_HOST ,
@@ -217,21 +218,18 @@ export class HttpPlugin extends BasePlugin {
217
218
'$1' ,
218
219
) ) ;
219
220
220
- rootSpan . addAttribute (
221
- HttpPlugin . ATTRIBUTE_HTTP_METHOD , request . method || 'GET' ) ;
222
-
221
+ rootSpan . addAttribute ( HttpPlugin . ATTRIBUTE_HTTP_METHOD , method ) ;
223
222
if ( requestUrl ) {
224
223
rootSpan . addAttribute (
225
224
HttpPlugin . ATTRIBUTE_HTTP_PATH , requestUrl . pathname || '' ) ;
226
225
rootSpan . addAttribute (
227
226
HttpPlugin . ATTRIBUTE_HTTP_ROUTE , requestUrl . path || '' ) ;
227
+ tags . set ( stats . HTTP_SERVER_ROUTE , { value : requestUrl . path || '' } ) ;
228
228
}
229
-
230
229
if ( userAgent ) {
231
230
rootSpan . addAttribute (
232
231
HttpPlugin . ATTRIBUTE_HTTP_USER_AGENT , userAgent ) ;
233
232
}
234
-
235
233
rootSpan . addAttribute (
236
234
HttpPlugin . ATTRIBUTE_HTTP_STATUS_CODE ,
237
235
response . statusCode . toString ( ) ) ;
@@ -243,6 +241,12 @@ export class HttpPlugin extends BasePlugin {
243
241
rootSpan . addMessageEvent (
244
242
MessageEventType . RECEIVED , uuid . v4 ( ) . split ( '-' ) . join ( '' ) ) ;
245
243
244
+ tags . set ( stats . HTTP_SERVER_METHOD , { value : method } ) ;
245
+ tags . set (
246
+ stats . HTTP_SERVER_STATUS ,
247
+ { value : response . statusCode . toString ( ) } ) ;
248
+ HttpPlugin . recordStats ( rootSpan . kind , tags , Date . now ( ) - startTime ) ;
249
+
246
250
rootSpan . end ( ) ;
247
251
return returned ;
248
252
} ;
@@ -253,20 +257,17 @@ export class HttpPlugin extends BasePlugin {
253
257
} ;
254
258
}
255
259
256
-
257
260
/**
258
261
* Creates spans for outgoing requests, sending spans' context for distributed
259
262
* tracing.
260
263
*/
261
264
protected getPatchOutgoingRequestFunction ( ) {
262
- return ( original : Func < httpModule . ClientRequest > ) : Func <
263
- httpModule . ClientRequest > => {
265
+ return ( original : Func < ClientRequest > ) : Func < ClientRequest > => {
264
266
const plugin = this ;
265
267
return function outgoingRequest (
266
- options : httpModule . RequestOptions | string ,
267
- callback ) : httpModule . ClientRequest {
268
+ options : RequestOptions | string , callback ) : ClientRequest {
268
269
if ( ! options ) {
269
- return original . apply ( this , arguments ) ;
270
+ return original . apply ( this , [ options , callback ] ) ;
270
271
}
271
272
272
273
// Makes sure the url is an url object
@@ -280,11 +281,10 @@ export class HttpPlugin extends BasePlugin {
280
281
origin = `${ parsedUrl . protocol || 'http:' } //${ parsedUrl . host } ` ;
281
282
} else {
282
283
// Do not trace ourselves
283
- if ( options . headers &&
284
- options . headers [ 'x-opencensus-outgoing-request' ] ) {
284
+ if ( isOpenCensusRequest ( options ) ) {
285
285
plugin . logger . debug (
286
286
'header with "x-opencensus-outgoing-request" - do not trace' ) ;
287
- return original . apply ( this , arguments ) ;
287
+ return original . apply ( this , [ options , callback ] ) ;
288
288
}
289
289
290
290
try {
@@ -294,12 +294,12 @@ export class HttpPlugin extends BasePlugin {
294
294
}
295
295
method = options . method || 'GET' ;
296
296
origin = `${ options . protocol || 'http:' } //${ options . host } ` ;
297
- } catch ( e ) {
297
+ } catch ( ignore ) {
298
298
}
299
299
}
300
300
301
- const request : httpModule . ClientRequest =
302
- original . apply ( this , arguments ) ;
301
+ const request : ClientRequest =
302
+ original . apply ( this , [ options , callback ] ) ;
303
303
304
304
if ( plugin . isIgnored (
305
305
origin + pathname , request ,
@@ -315,7 +315,6 @@ export class HttpPlugin extends BasePlugin {
315
315
kind : SpanKind . CLIENT ,
316
316
} ;
317
317
318
-
319
318
// Checks if this outgoing request is part of an operation by checking
320
319
// if there is a current root span, if so, we create a child span. In
321
320
// case there is no root span, this means that the outgoing request is
@@ -343,10 +342,10 @@ export class HttpPlugin extends BasePlugin {
343
342
* @param options The arguments to the original function.
344
343
*/
345
344
private getMakeRequestTraceFunction (
346
- // tslint:disable-next-line:no-any
347
- request : httpModule . ClientRequest , options : httpModule . RequestOptions ,
348
- plugin : HttpPlugin ) : Func < httpModule . ClientRequest > {
349
- return ( span : Span ) : httpModule . ClientRequest => {
345
+ request : ClientRequest , options : RequestOptions ,
346
+ plugin : HttpPlugin ) : Func < ClientRequest > {
347
+ return ( span : Span ) : ClientRequest = > {
348
+ const startTime = Date . now ( ) ;
350
349
plugin . logger . debug ( 'makeRequestTrace' ) ;
351
350
352
351
if ( ! span ) {
@@ -365,17 +364,19 @@ export class HttpPlugin extends BasePlugin {
365
364
propagation . inject ( setter , span . spanContext ) ;
366
365
}
367
366
368
- request . on ( 'response' , ( response : httpModule . ClientResponse ) => {
367
+ request . on ( 'response' , ( response : ClientResponse ) => {
369
368
plugin . tracer . wrapEmitter ( response ) ;
370
369
plugin . logger . debug ( 'outgoingRequest on response()' ) ;
371
-
372
370
response . on ( 'end' , ( ) => {
373
371
plugin . logger . debug ( 'outgoingRequest on end()' ) ;
374
372
const method = response . method ? response . method : 'GET' ;
375
373
const headers = options . headers ;
376
374
const userAgent =
377
375
headers ? ( headers [ 'user-agent' ] || headers [ 'User-Agent' ] ) : null ;
378
376
377
+ const tags = new TagMap ( ) ;
378
+ tags . set ( stats . HTTP_CLIENT_METHOD , { value : method } ) ;
379
+
379
380
if ( options . hostname ) {
380
381
span . addAttribute ( HttpPlugin . ATTRIBUTE_HTTP_HOST , options . hostname ) ;
381
382
}
@@ -394,12 +395,16 @@ export class HttpPlugin extends BasePlugin {
394
395
HttpPlugin . ATTRIBUTE_HTTP_STATUS_CODE ,
395
396
response . statusCode . toString ( ) ) ;
396
397
span . setStatus ( HttpPlugin . parseResponseStatus ( response . statusCode ) ) ;
398
+ tags . set (
399
+ stats . HTTP_CLIENT_STATUS ,
400
+ { value : response . statusCode . toString ( ) } ) ;
397
401
}
398
402
399
403
// Message Event ID is not defined
400
404
span . addMessageEvent (
401
405
MessageEventType . SENT , uuid . v4 ( ) . split ( '-' ) . join ( '' ) ) ;
402
406
407
+ HttpPlugin . recordStats ( span . kind , tags , Date . now ( ) - startTime ) ;
403
408
span . end ( ) ;
404
409
} ) ;
405
410
@@ -457,6 +462,30 @@ export class HttpPlugin extends BasePlugin {
457
462
}
458
463
}
459
464
}
465
+
466
+ /** Method to record stats for client and server. */
467
+ static recordStats ( kind : SpanKind , tags : TagMap , ms : number ) {
468
+ if ( ! plugin . stats ) {
469
+ return ;
470
+ }
471
+
472
+ try {
473
+ const measureList = [ ] ;
474
+ switch ( kind ) {
475
+ case SpanKind . CLIENT :
476
+ measureList . push (
477
+ { measure : stats . HTTP_CLIENT_ROUNDTRIP_LATENCY , value : ms } ) ;
478
+ break ;
479
+ case SpanKind . SERVER :
480
+ measureList . push ( { measure : stats . HTTP_SERVER_LATENCY , value : ms } ) ;
481
+ break ;
482
+ default :
483
+ break ;
484
+ }
485
+ plugin . stats . record ( measureList , tags ) ;
486
+ } catch ( ignore ) {
487
+ }
488
+ }
460
489
}
461
490
462
491
const plugin = new HttpPlugin ( 'http' ) ;
0 commit comments