/**
 * 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 { Logger , Plugin , PluginNames , Tracer , PluginConfig } from '@opencensus/core' ; import * as fs from 'fs' ; import * as path from 'path' ; import * as hook from 'require-in-the-middle' ; import { Constants } from '../constants' ; enum HookState { UNINITIALIZED , ENABLED , DISABLED } /**
 * The PluginLoader class can load instrumentation plugins that
 * use a patch mechanism to enable automatic tracing for
 * specific target modules.
 */ export class PluginLoader { /** The tracer */ private tracer : Tracer ; /** logger */ private logger : Logger ; /** A list of loaded plugins. */ plugins : Plugin [ ] = [ ] ; /**
     * A field that tracks whether the r-i-t-m hook has been loaded for the
     * first time, as well as whether the hook body is enabled or not.
     */ private hookState = HookState . UNINITIALIZED ; param tracer constructor ( logger , tracer ) { this . tracer = tracer ; this . logger = logger ; } param moduleName returns private static defaultPackageName ( moduleName ) { return `${ Constants . OPENCENSUS_SCOPE }/${ Constants . DEFAULT_PLUGIN_PACKAGE_NAME_PREFIX }-${ moduleName }` ; } param modulesToPatch returns static defaultPluginsFromArray ( modulesToPatch ) { const plugins = modulesToPatch . reduce ( ( plugins , moduleName ) => { plugins [ moduleName ] = PluginLoader . defaultPackageName ( moduleName ) ; return plugins ; } , { } as PluginNames ) ; return plugins ; } param name param basedir private getPackageVersion ( name , basedir ) { let version = '' ; if ( basedir ) { const pkgJson = path . join ( basedir , 'package.json' ) ; try { version = JSON . parse ( fs . readFileSync ( pkgJson ) . toString ( ) ) . version ; } catch ( e ) { this . logger . error ( 'could not get version of %s module: %s' , name , e . message ) ; } } else { version = process . versions . node ; } return version ; } param pluginList loadPlugins ( pluginList ) { if ( this . hookState === HookState . UNINITIALIZED ) { hook ( Object . keys ( pluginList ) , ( exports , name , basedir ) => { if ( this . hookState !== HookState . ENABLED ) { return exports ; } const plugin = pluginList [ name ] ; const version = this . getPackageVersion ( name , basedir as string ) ; this . logger . info ( 'trying loading %s.%s' , name , version ) ; if ( ! version ) { return exports ; } this . logger . debug ( 'applying patch to %s@%s module' , name , version ) ; let moduleName ; let moduleConfig = { } ; if ( typeof plugin === 'string' ) { moduleName = plugin ; } else { moduleConfig = plugin . config ; moduleName = plugin . module ; } this . logger . debug ( 'using package %s to patch %s' , moduleName , name ) ; try { const plugin = require ( moduleName as string ) . plugin ; this . plugins . push ( plugin ) ; return plugin . enable ( exports , this . tracer , version , moduleConfig , basedir ) ; } catch ( e ) { this . logger . error ( 'could not load plugin %s of module %s. Error: %s' , moduleName , name , e . message ) ; return exports ; } } ) ; } this . hookState = HookState . ENABLED ; } /** Unloads plugins. */ unloadPlugins ( ) { for ( const plugin of this . plugins ) { plugin . disable ( ) ; } this . plugins = [ ] ; this . hookState = HookState . DISABLED ; } param searchPath static set searchPathForTest ( searchPath ) { module . paths . push ( searchPath ) ; } }