'use strict' ; import { BasePlugin , Span , SpanKind } from '@opencensus/core' ; import * as shimmer from 'shimmer' ; import * as semver from 'semver' ; import * as redis from 'redis' ; export type Redis = typeof redis ; export type IgnoreMatcher = string | RegExp ; export type RedisPluginConfig = { /**
     * Add arguments to the span metadata for a every command
     */ detailedCommands : boolean ; } ; /** Redis instrumentation plugin for Opencensus */ export class RedisPlugin extends BasePlugin { protected options : RedisPluginConfig ; /** Constructs a new Redis instance. */ constructor ( moduleName ) { super ( moduleName ) ; } /**
     * Patches Redis operations.
     */ protected applyPatch ( ) { this . logger . debug ( 'Patched redis' ) ; if ( semver . lt ( this . version , '2.4.0' ) ) { this . logger . info ( 'disabling redis plugin because version isnt supported' ) ; return this . moduleExports ; } if ( this . moduleExports . RedisClient ) { this . logger . debug ( 'patching redis.RedisClient.prototype.create_stream' ) ; shimmer . wrap ( this . moduleExports . RedisClient . prototype , 'create_stream' , this . getPatchCreateStream ( ) ) ; this . logger . debug ( 'patching redis.RedisClient.prototype.internal_send' ) ; shimmer . wrap ( this . moduleExports . RedisClient . prototype , 'internal_send_command' , this . getPatchSendCommand ( ) ) ; this . logger . debug ( 'patching redis.RedisClient.prototype.createClient' ) ; shimmer . wrap ( this . moduleExports , 'createClient' , this . getPatchCreateClient ( ) ) ; } return this . moduleExports ; } /** Unpatches all Redis patched functions. */ applyUnpatch ( ) { if ( semver . lt ( this . version , '2.4.0' ) ) return ; shimmer . unwrap ( this . moduleExports . RedisClient . prototype , 'internal_send_command' ) ; shimmer . unwrap ( this . moduleExports , 'createClient' ) ; shimmer . unwrap ( this . moduleExports . RedisClient . prototype , 'create_stream' ) ; } /** Patch internal event emmitter to propagate the context */ private getPatchCreateStream ( ) { const plugin = this ; return function createStreamWrap ( original ) { return function create_stream_trace ( ) { if ( ! this . stream ) { Object . defineProperty ( this , 'stream' , { get : function ( ) { return this . _patched_redis_stream ; } , set : function ( val ) { plugin . tracer . wrapEmitter ( val ) ; this . _patched_redis_stream = val ; } } ) ; } return original . apply ( this , arguments ) ; } ; } ; } /** Patch client event emmitter to propagate the context */ private getPatchCreateClient ( ) { const plugin = this ; return function createClientWrap ( original ) { return function createClientTrace ( ) { const client = original . apply ( this , arguments ) ; plugin . tracer . wrapEmitter ( client ) ; return client ; } ; } ; } /** Patch send command internal to trace requests */ private getPatchSendCommand ( ) { const plugin = this ; const addArguments = typeof this . options === 'object' && this . options . detailedCommands === true ; return function internalSendCommandWrap ( original ) { return function internal_send_command_trace ( cmd , args , cb ) { if ( ! plugin . tracer . currentRootSpan ) { return original . apply ( this , arguments ) ; } if ( arguments . length === 1 && typeof cmd === 'object' ) { const span = plugin . tracer . startChildSpan ( `redis-${ cmd . command }` , SpanKind . CLIENT ) ; if ( span === null ) return original . apply ( this , arguments ) ; span . addAttribute ( 'command' , cmd . command ) ; if ( addArguments ) { span . addAttribute ( 'arguments' , JSON . stringify ( cmd . args || [ ] ) ) ; } cmd . callback = plugin . patchEnd ( span , cmd . callback ) ; return original . apply ( this , arguments ) ; } if ( typeof cmd === 'string' && Array . isArray ( args ) && typeof cb === 'function' ) { const span = plugin . tracer . startChildSpan ( `redis-${ cmd }` , SpanKind . CLIENT ) ; if ( span === null ) return original . apply ( this , arguments ) ; span . addAttribute ( 'command' , cmd ) ; if ( addArguments ) { span . addAttribute ( 'arguments' , JSON . stringify ( args ) ) ; } cb = plugin . patchEnd ( span , cb ) ; return original . apply ( this , arguments ) ; } return original . apply ( this , arguments ) ; } ; } ; } param span param resultHandler patchEnd ( span , resultHandler ) { const plugin = this ; const patchedEnd = function ( err ? ) { if ( plugin . options . detailedCommands === true && err instanceof Error ) { span . addAttribute ( 'error' , err . message ) ; } if ( span . ended === false ) { span . end ( ) ; } if ( typeof resultHandler === 'function' ) { return resultHandler . apply ( this , arguments ) ; } } ; return this . tracer . wrap ( patchedEnd ) ; } } const plugin = new RedisPlugin ( 'redis' ) ; export { plugin } ;