/**
 * Copyright 2018, OpenCensus Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { BasePlugin , TraceOptions , SpanKind } from '@opencensus/core' ; import * as netModule from 'net' ; import * as shimmer from 'shimmer' ; /** Http instrumentation plugin for Opencensus */ export class NetPlugin extends BasePlugin { /** Constructs a new HttpPlugin instance. */ constructor ( moduleName ) { super ( moduleName ) ; } /**
     * Patches HTTP incoming and outcoming request functions.
     */ protected applyPatch ( ) { this . logger . debug ( 'applying patch to %s@%s' , this . moduleName , this . version ) ; if ( this . moduleExports && this . moduleExports . Server && this . moduleExports . Server . prototype ) { shimmer . wrap ( this . moduleExports . Server . prototype , 'emit' , this . getPatchIncomingRequestFunction ( ) ) ; } else { this . logger . error ( 'Could not apply patch to %s.emit. Interface is not as expected.' , this . moduleName ) ; } return this . moduleExports ; } /** Unpatches all HTTP patched function. */ applyUnpatch ( ) { if ( this . moduleExports && this . moduleExports . Server && this . moduleExports . Server . prototype ) { shimmer . unwrap ( this . moduleExports . Server . prototype , 'emit' ) ; } else { this . logger . error ( 'Could not unapply patch to %s.emit. Interface is not as expected.' , this . moduleName ) ; } } /**
     * Creates spans for incoming requests, restoring spans' context if applied.
     */ protected getPatchIncomingRequestFunction ( ) { return ( original ) => { const plugin = this ; return function incomingRequest ( event , ... args ) : boolean { if ( event !== 'connection' ) { return original . apply ( this , arguments ) ; } const socket = args [ 0 ] ; plugin . logger . debug ( '%s plugin incomingRequest' , plugin . moduleName ) ; const traceOptions = { name : 'socket' , kind : SpanKind . SERVER , spanContext : undefined } ; return plugin . tracer . startRootSpan ( traceOptions , rootSpan => { if ( ! rootSpan ) return original . apply ( this , arguments ) ; plugin . tracer . wrapEmitter ( socket ) ; const address = socket . address ( ) ; if ( typeof address === 'string' ) { rootSpan . addAttribute ( 'net.address' , address ) ; } else { rootSpan . addAttribute ( 'net.host' , address . address ) ; rootSpan . addAttribute ( 'net.port' , address . port ) ; rootSpan . addAttribute ( 'net.family' , address . family ) ; } socket . on ( 'error' , ( err ) => { rootSpan . addAttribute ( 'net.error' , err . message ) ; } ) ; const originalEnd = socket . end ; socket . end = function ( ) { if ( rootSpan . ended === false ) { rootSpan . end ( ) ; } return originalEnd . apply ( this , arguments ) ; } ; socket . on ( 'close' , ( ) => { if ( rootSpan . ended === false ) { rootSpan . end ( ) ; } } ) ; return original . apply ( this , arguments ) ; } ) ; } ; } ; } } export const plugin = new NetPlugin ( 'net' ) ;