'use strict' ; import Configuration from './configuration' ; import Debug from 'debug' ; import { ServiceManager } from './serviceManager' ; import { createTransport , TransportConfig , Transport } from './services/transport' ; import { FeatureManager } from './featureManager' ; import { ActionService } from './services/actions' ; import { NotifyFeature , ErrorContext } from './features/notify' ; import { Metric , MetricService , HistogramOptions , MetricBulk , MetricType , MetricMeasurements } from './services/metrics' ; import Meter from './utils/metrics/meter' ; import Histogram from './utils/metrics/histogram' ; import Gauge from './utils/metrics/gauge' ; import Counter from './utils/metrics/counter' ; import { EventsFeature } from './features/events' ; import { TracingConfig , TracingFeature } from './features/tracing' ; import { InspectorService } from './services/inspector' ; import { canUseInspector } from './constants' ; import { MetricConfig } from './features/metrics' ; import { ProfilingConfig } from './features/profiling' ; import { RuntimeStatsService } from './services/runtimeStats' ; import { Entrypoint } from './features/entrypoint' ; import { Tracer } from '@opencensus/core' ; export class IOConfig { /**
     * Automatically catch unhandled errors
     */ catchExceptions ? : boolean = true ; /**
     * Configure the metrics to add automatically to your process
     */ metrics ? : MetricConfig ; /**
     * Configure the default actions that you can run
     */ actions ? : { eventLoopDump ? : boolean ; } ; /**
     * Configure availables profilers that will be exposed
     */ profiling ? : ProfilingConfig | boolean = true ; /**
     * Configure the transaction tracing options
     */ tracing ? : TracingConfig | boolean = false ; /**
     * If you want to connect to PM2 Enterprise without using PM2, you should enable
     * the standalone mode
     */ standalone ? : boolean = false ; /**
     * Define custom options for the standalone mode
     */ apmOptions ? : TransportConfig ; } export const defaultConfig = { catchExceptions : true , profiling : true , metrics : { v8 : true , network : false , eventLoop : true , runtime : true , http : true } , standalone : false , apmOptions : undefined , tracing : { enabled : false , outbound : false } } ; export default class PMX { private initialConfig : IOConfig ; private featureManager : FeatureManager = new FeatureManager ( ) ; private transport : Transport | null = null ; private actionService : ActionService | null = null ; private metricService : MetricService | null = null ; private runtimeStatsService : RuntimeStatsService | null = null ; private logger : Function = Debug ( 'axm:main' ) ; private initialized : boolean = false ; public Entrypoint : { new ( ) : Entrypoint ; } = Entrypoint ; /**
     * Init the APM instance, you should *always* init it before using any method
     */ init ( config ? ) { const callsite = ( new Error ( ) . stack || '' ) . split ( '\n' ) [ 2 ] ; if ( callsite && callsite . length > 0 ) { this . logger ( `init from ${ callsite }` ) ; } if ( this . initialized === true ) { this . logger ( `Calling init but was already the case, destroying and recreating` ) ; this . destroy ( ) ; } if ( config === undefined ) { config = defaultConfig ; } if ( ! config . standalone ) { const autoStandalone = process . env . PM2_SECRET_KEY && process . env . PM2_PUBLIC_KEY && process . env . PM2_APP_NAME ; config . standalone = ! ! autoStandalone ; config . apmOptions = autoStandalone ? { secretKey : process . env . PM2_SECRET_KEY , publicKey : process . env . PM2_PUBLIC_KEY , appName : process . env . PM2_APP_NAME } as TransportConfig : undefined ; } this . transport = createTransport ( config . standalone === true ? 'websocket' : 'ipc' , config . apmOptions as TransportConfig ) ; ServiceManager . set ( 'transport' , this . transport ) ; if ( canUseInspector ( ) ) { const Inspector = require ( './services/inspector' ) ; const inspectorService = new Inspector ( ) ; inspectorService . init ( ) ; ServiceManager . set ( 'inspector' , inspectorService ) ; } this . actionService = new ActionService ( ) ; this . actionService . init ( ) ; ServiceManager . set ( 'actions' , this . actionService ) ; this . metricService = new MetricService ( ) ; this . metricService . init ( ) ; ServiceManager . set ( 'metrics' , this . metricService ) ; this . runtimeStatsService = new RuntimeStatsService ( ) ; this . runtimeStatsService . init ( ) ; if ( this . runtimeStatsService . isEnabled ( ) ) { ServiceManager . set ( 'runtimeStats' , this . runtimeStatsService ) ; } this . featureManager . init ( config ) ; Configuration . init ( config ) ; this . initialConfig = config ; this . initialized = true ; return this ; } /**
     * Destroy the APM instance, every method will stop working afterwards
     */ destroy ( ) { this . logger ( 'destroy' ) ; this . featureManager . destroy ( ) ; if ( this . actionService !== null ) { this . actionService . destroy ( ) ; } if ( this . transport !== null ) { this . transport . destroy ( ) ; } if ( this . metricService !== null ) { this . metricService . destroy ( ) ; } if ( this . runtimeStatsService !== null ) { this . runtimeStatsService . destroy ( ) ; } const inspectorService = ServiceManager . get ( 'inspector' ) ; if ( inspectorService !== undefined ) { inspectorService . destroy ( ) ; } } /**
     * Fetch current configuration of the APM
     */ getConfig ( ) { return this . initialConfig ; } /**
     * Notify an error to PM2 Plus/Enterprise, note that you can attach a context to it
     * to provide more insight about the error
     */ notifyError ( error , context ? ) { const notify = this . featureManager . get ( 'notify' ) as NotifyFeature ; return notify . notifyError ( error , context ) ; } /**
     * Register metrics in bulk
     */ metrics ( metric ) { const res = [ ] ; if ( metric === undefined || metric === null ) { console . error ( `Received empty metric to create` ) ; console . trace ( ) ; return [ ] ; } let metrics = ! Array . isArray ( metric ) ? [ metric ] : metric ; for ( let metric of metrics ) { if ( typeof metric . name !== 'string' ) { console . error ( `Trying to create a metrics without a name` , metric ) ; console . trace ( ) ; res . push ( { } ) ; continue ; } if ( metric . type === undefined ) { metric . type = MetricType . gauge ; } switch ( metric . type ) { case MetricType . counter : { res . push ( this . counter ( metric ) ) ; continue ; } case MetricType . gauge : { res . push ( this . gauge ( metric ) ) ; continue ; } case MetricType . histogram : { res . push ( this . histogram ( metric as any ) ) ; continue ; } case MetricType . meter : { res . push ( this . meter ( metric ) ) ; continue ; } case MetricType . metric : { res . push ( this . gauge ( metric ) ) ; continue ; } default : { console . error ( `Invalid metric type ${ metric . type } for metric ${ metric . name }` ) ; console . trace ( ) ; res . push ( { } ) ; continue ; } } } return res ; } /**
     * Create an histogram metric
     */ histogram ( config ) { if ( typeof config === 'string' ) { config = { name : config as string , measurement : MetricMeasurements . mean } ; } if ( this . metricService === null ) { return console . trace ( `Tried to register a metric without initializing @pm2/io` ) ; } return this . metricService . histogram ( config ) ; } /**
     * Create a gauge metric
     */ metric ( config ) { if ( typeof config === 'string' ) { config = { name : config as string } ; } if ( this . metricService === null ) { return console . trace ( `Tried to register a metric without initializing @pm2/io` ) ; } return this . metricService . metric ( config ) ; } /**
     * Create a gauge metric
     */ gauge ( config ) { if ( typeof config === 'string' ) { config = { name : config as string } ; } if ( this . metricService === null ) { return console . trace ( `Tried to register a metric without initializing @pm2/io` ) ; } return this . metricService . metric ( config ) ; } /**
     * Create a counter metric
     */ counter ( config ) { if ( typeof config === 'string' ) { config = { name : config as string } ; } if ( this . metricService === null ) { return console . trace ( `Tried to register a metric without initializing @pm2/io` ) ; } return this . metricService . counter ( config ) ; } /**
     * Create a meter metric
     */ meter ( config ) { if ( typeof config === 'string' ) { config = { name : config as string } ; } if ( this . metricService === null ) { return console . trace ( `Tried to register a metric without initializing @pm2/io` ) ; } return this . metricService . meter ( config ) ; } /**
     * Register a custom action that will be executed when the someone called
     * it from the API
     */ action ( name , opts ? , fn ? ) { if ( typeof name === 'object' ) { const tmp = name ; name = tmp . name ; opts = tmp . options ; fn = tmp . action ; } if ( this . actionService === null ) { return console . trace ( `Tried to register a action without initializing @pm2/io` ) ; } return this . actionService . registerAction ( name , opts , fn ) ; } onExit ( callback ) { if ( typeof callback === 'function' ) { const onExit = require ( 'signal-exit' ) ; return onExit ( callback ) ; } } deprecated emit ( name , data ) { const events = this . featureManager . get ( 'events' ) as EventsFeature ; return events . emit ( name , data ) ; } /**
     * Get the tracing agent to add more information about traces
     */ getTracer ( ) { const tracing = this . featureManager . get ( 'tracing' ) as TracingFeature ; return tracing . getTracer ( ) ; } initModule ( opts , cb ? ) { if ( ! opts ) opts = { } ; if ( opts . reference ) { opts . name = opts . reference ; delete opts . reference ; } opts = Object . assign ( { widget : { } } , opts ) ; opts . widget = Object . assign ( { type : 'generic' , logo : 'https://app.keymetrics.io/img/logo/keymetrics-300.png' , theme : [ '#111111' , '#1B2228' , '#807C7C' , '#807C7C' ] } , opts . widget ) ; opts . isModule = true ; opts = Configuration . init ( opts ) ; return typeof cb === 'function' ? cb ( null , opts ) : opts ; } /**
     * Return a custom express middleware that will send an error to the backend
     * with all the details of the http request
     */ expressErrorHandler ( ) { const notify = this . featureManager . get ( 'notify' ) as NotifyFeature ; return notify . expressErrorHandler ( ) ; } /**
     * Return a custom koa middleware that will send an error to the backend
     * with all the details of the http request
     */ koaErrorHandler ( ) { const notify = this . featureManager . get ( 'notify' ) as NotifyFeature ; return notify . koaErrorHandler ( ) ; } }