1414 * limitations under the License.
1515 */
1616
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' ;
1919import * as semver from 'semver' ;
2020import * as shimmer from 'shimmer' ;
2121import * as url from 'url' ;
2222import * as uuid from 'uuid' ;
23- import { HttpPluginConfig , IgnoreMatcher } from './types' ;
23+ import * as stats from './http-stats' ;
24+ import { IgnoreMatcher } from './types' ;
2425
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+ }
2833
2934/** Http instrumentation plugin for Opencensus */
3035export class HttpPlugin extends BasePlugin {
@@ -47,10 +52,7 @@ export class HttpPlugin extends BasePlugin {
4752 super ( moduleName ) ;
4853 }
4954
50-
51- /**
52- * Patches HTTP incoming and outcoming request functions.
53- */
55+ /** Patches HTTP incoming and outcoming request functions. */
5456 protected applyPatch ( ) {
5557 this . logger . debug ( 'applying patch to %s@%s' , this . moduleName , this . version ) ;
5658
@@ -72,9 +74,8 @@ export class HttpPlugin extends BasePlugin {
7274 // https://nodejs.org/dist/latest/docs/api/http.html#http_http_get_options_callback
7375 // https://github.com/googleapis/cloud-trace-nodejs/blob/master/src/plugins/plugin-http.ts#L198
7476 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 ) ;
7879 req . end ( ) ;
7980 return req ;
8081 } ;
@@ -95,7 +96,6 @@ export class HttpPlugin extends BasePlugin {
9596 return this . moduleExports ;
9697 }
9798
98-
9999 /** Unpatches all HTTP patched function. */
100100 protected applyUnpatch ( ) : void {
101101 shimmer . unwrap ( this . moduleExports , 'request' ) ;
@@ -149,7 +149,6 @@ export class HttpPlugin extends BasePlugin {
149149 }
150150 }
151151
152-
153152 /**
154153 * Creates spans for incoming requests, restoring spans' context if applied.
155154 */
@@ -164,10 +163,11 @@ export class HttpPlugin extends BasePlugin {
164163 if ( event !== 'request' ) {
165164 return original . apply ( this , arguments ) ;
166165 }
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 ] ;
170169 const path = request . url ? url . parse ( request . url ) . pathname || '' : '' ;
170+ const method = request . method || 'GET' ;
171171 plugin . logger . debug ( '%s plugin incomingRequest' , plugin . moduleName ) ;
172172
173173 if ( plugin . isIgnored (
@@ -201,14 +201,15 @@ export class HttpPlugin extends BasePlugin {
201201 // https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/plugins/plugin-connect.ts#L75)
202202 const originalEnd = response . end ;
203203
204- response . end = function ( this : httpModule . ServerResponse ) {
204+ response . end = function ( this : ServerResponse ) {
205205 response . end = originalEnd ;
206206 const returned = response . end . apply ( this , arguments ) ;
207207
208208 const requestUrl = request . url ? url . parse ( request . url ) : null ;
209209 const host = headers . host || 'localhost' ;
210210 const userAgent =
211211 ( headers [ 'user-agent' ] || headers [ 'User-Agent' ] ) as string ;
212+ const tags = new TagMap ( ) ;
212213
213214 rootSpan . addAttribute (
214215 HttpPlugin . ATTRIBUTE_HTTP_HOST ,
@@ -217,21 +218,18 @@ export class HttpPlugin extends BasePlugin {
217218 '$1' ,
218219 ) ) ;
219220
220- rootSpan . addAttribute (
221- HttpPlugin . ATTRIBUTE_HTTP_METHOD , request . method || 'GET' ) ;
222-
221+ rootSpan . addAttribute ( HttpPlugin . ATTRIBUTE_HTTP_METHOD , method ) ;
223222 if ( requestUrl ) {
224223 rootSpan . addAttribute (
225224 HttpPlugin . ATTRIBUTE_HTTP_PATH , requestUrl . pathname || '' ) ;
226225 rootSpan . addAttribute (
227226 HttpPlugin . ATTRIBUTE_HTTP_ROUTE , requestUrl . path || '' ) ;
227+ tags . set ( stats . HTTP_SERVER_ROUTE , { value : requestUrl . path || '' } ) ;
228228 }
229-
230229 if ( userAgent ) {
231230 rootSpan . addAttribute (
232231 HttpPlugin . ATTRIBUTE_HTTP_USER_AGENT , userAgent ) ;
233232 }
234-
235233 rootSpan . addAttribute (
236234 HttpPlugin . ATTRIBUTE_HTTP_STATUS_CODE ,
237235 response . statusCode . toString ( ) ) ;
@@ -243,6 +241,12 @@ export class HttpPlugin extends BasePlugin {
243241 rootSpan . addMessageEvent (
244242 MessageEventType . RECEIVED , uuid . v4 ( ) . split ( '-' ) . join ( '' ) ) ;
245243
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+
246250 rootSpan . end ( ) ;
247251 return returned ;
248252 } ;
@@ -253,20 +257,17 @@ export class HttpPlugin extends BasePlugin {
253257 } ;
254258 }
255259
256-
257260 /**
258261 * Creates spans for outgoing requests, sending spans' context for distributed
259262 * tracing.
260263 */
261264 protected getPatchOutgoingRequestFunction ( ) {
262- return ( original : Func < httpModule . ClientRequest > ) : Func <
263- httpModule . ClientRequest > => {
265+ return ( original : Func < ClientRequest > ) : Func < ClientRequest > => {
264266 const plugin = this ;
265267 return function outgoingRequest (
266- options : httpModule . RequestOptions | string ,
267- callback ) : httpModule . ClientRequest {
268+ options : RequestOptions | string , callback ) : ClientRequest {
268269 if ( ! options ) {
269- return original . apply ( this , arguments ) ;
270+ return original . apply ( this , [ options , callback ] ) ;
270271 }
271272
272273 // Makes sure the url is an url object
@@ -280,11 +281,10 @@ export class HttpPlugin extends BasePlugin {
280281 origin = `${ parsedUrl . protocol || 'http:' } //${ parsedUrl . host } ` ;
281282 } else {
282283 // Do not trace ourselves
283- if ( options . headers &&
284- options . headers [ 'x-opencensus-outgoing-request' ] ) {
284+ if ( isOpenCensusRequest ( options ) ) {
285285 plugin . logger . debug (
286286 'header with "x-opencensus-outgoing-request" - do not trace' ) ;
287- return original . apply ( this , arguments ) ;
287+ return original . apply ( this , [ options , callback ] ) ;
288288 }
289289
290290 try {
@@ -294,12 +294,12 @@ export class HttpPlugin extends BasePlugin {
294294 }
295295 method = options . method || 'GET' ;
296296 origin = `${ options . protocol || 'http:' } //${ options . host } ` ;
297- } catch ( e ) {
297+ } catch ( ignore ) {
298298 }
299299 }
300300
301- const request : httpModule . ClientRequest =
302- original . apply ( this , arguments ) ;
301+ const request : ClientRequest =
302+ original . apply ( this , [ options , callback ] ) ;
303303
304304 if ( plugin . isIgnored (
305305 origin + pathname , request ,
@@ -315,7 +315,6 @@ export class HttpPlugin extends BasePlugin {
315315 kind : SpanKind . CLIENT ,
316316 } ;
317317
318-
319318 // Checks if this outgoing request is part of an operation by checking
320319 // if there is a current root span, if so, we create a child span. In
321320 // case there is no root span, this means that the outgoing request is
@@ -343,10 +342,10 @@ export class HttpPlugin extends BasePlugin {
343342 * @param options The arguments to the original function.
344343 */
345344 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 ( ) ;
350349 plugin . logger . debug ( 'makeRequestTrace' ) ;
351350
352351 if ( ! span ) {
@@ -365,17 +364,19 @@ export class HttpPlugin extends BasePlugin {
365364 propagation . inject ( setter , span . spanContext ) ;
366365 }
367366
368- request . on ( 'response' , ( response : httpModule . ClientResponse ) => {
367+ request . on ( 'response' , ( response : ClientResponse ) => {
369368 plugin . tracer . wrapEmitter ( response ) ;
370369 plugin . logger . debug ( 'outgoingRequest on response()' ) ;
371-
372370 response . on ( 'end' , ( ) => {
373371 plugin . logger . debug ( 'outgoingRequest on end()' ) ;
374372 const method = response . method ? response . method : 'GET' ;
375373 const headers = options . headers ;
376374 const userAgent =
377375 headers ? ( headers [ 'user-agent' ] || headers [ 'User-Agent' ] ) : null ;
378376
377+ const tags = new TagMap ( ) ;
378+ tags . set ( stats . HTTP_CLIENT_METHOD , { value : method } ) ;
379+
379380 if ( options . hostname ) {
380381 span . addAttribute ( HttpPlugin . ATTRIBUTE_HTTP_HOST , options . hostname ) ;
381382 }
@@ -394,12 +395,16 @@ export class HttpPlugin extends BasePlugin {
394395 HttpPlugin . ATTRIBUTE_HTTP_STATUS_CODE ,
395396 response . statusCode . toString ( ) ) ;
396397 span . setStatus ( HttpPlugin . parseResponseStatus ( response . statusCode ) ) ;
398+ tags . set (
399+ stats . HTTP_CLIENT_STATUS ,
400+ { value : response . statusCode . toString ( ) } ) ;
397401 }
398402
399403 // Message Event ID is not defined
400404 span . addMessageEvent (
401405 MessageEventType . SENT , uuid . v4 ( ) . split ( '-' ) . join ( '' ) ) ;
402406
407+ HttpPlugin . recordStats ( span . kind , tags , Date . now ( ) - startTime ) ;
403408 span . end ( ) ;
404409 } ) ;
405410
@@ -457,6 +462,30 @@ export class HttpPlugin extends BasePlugin {
457462 }
458463 }
459464 }
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+ }
460489}
461490
462491const plugin = new HttpPlugin ( 'http' ) ;
0 commit comments