import { StatsD } from "hot-shots" ; import { promisify } from "util" ; import { logDebug , logError } from "../utils" ; import { APIClient } from "./api" ; import { flushExtension , isAgentRunning } from "./extension" ; import { KMSService } from "./kms-service" ; import { writeMetricToStdout } from "./metric-log" ; import { Distribution } from "./model" ; import { Processor } from "./processor" ; const metricsBatchSendIntervalMS = 10000 ; export interface MetricsConfig { default shouldRetryMetrics : boolean ; /**
     * The api key used to talk to the Datadog API. If this is empty, apiKeyKMS key
     * will be used instead.
     */ apiKey : string ; /**
     * A KMS encrypted api key used to talk to the Datadog API. It will automatically
     * be decrypted before any metrics are sent.
     */ apiKeyKMS : string ; default siteURL : string ; default logForwarding : boolean ; default enhancedMetrics : boolean ; } export class MetricsListener { private currentProcessor ? : Promise < Processor > ; private apiKey : Promise < string > ; private statsDClient ? : StatsD ; private isAgentRunning ? : boolean = undefined ; constructor ( private kmsClient , private config ) { this . apiKey = this . getAPIKey ( config ) ; } public async onStartInvocation ( _ ) { if ( this . isAgentRunning === undefined ) { this . isAgentRunning = await isAgentRunning ( ) ; logDebug ( `Extension present: ${ this . isAgentRunning }` ) ; } if ( this . isAgentRunning ) { logDebug ( `Using StatsD client` ) ; this . statsDClient = new StatsD ( { host : "127.0.0.1" , closingFlushInterval : 1 } ) ; return ; } if ( this . config . logForwarding ) { logDebug ( `logForwarding configured` ) ; return ; } this . currentProcessor = this . createProcessor ( this . config , this . apiKey ) ; } public async onCompleteInvocation ( ) { try { if ( this . currentProcessor !== undefined ) { const processor = await this . currentProcessor ; await promisify ( setImmediate ) ( ) ; await processor . flush ( ) ; } if ( this . statsDClient !== undefined ) { logDebug ( `Flushing statsD` ) ; await new Promise < void > ( ( resolve , reject ) => { this . statsDClient ?. close ( ( error ) => { if ( error !== undefined ) { reject ( error ) ; } resolve ( ) ; } ) ; } ) ; this . statsDClient = undefined ; } } catch ( error ) { if ( error instanceof Error ) { logError ( "failed to flush metrics" , error as Error ) ; } } try { if ( this . isAgentRunning ) { logDebug ( `Flushing Extension` ) ; await flushExtension ( ) ; } } catch ( error ) { if ( error instanceof Error ) { logError ( "failed to flush extension" , error as Error ) ; } } this . currentProcessor = undefined ; } public sendDistributionMetricWithDate ( name , value , metricTime , forceAsync , ... tags ) { if ( this . isAgentRunning ) { this . statsDClient ?. distribution ( name , value , undefined , tags ) ; return ; } if ( this . config . logForwarding || forceAsync ) { writeMetricToStdout ( name , value , metricTime , tags ) ; return ; } const dist = new Distribution ( name , [ { timestamp : metricTime , value } ] , ... tags ) ; if ( this . currentProcessor !== undefined ) { this . currentProcessor . then ( ( processor ) => { processor . addMetric ( dist ) ; } ) ; } else { logError ( "can't send metrics, datadog lambda handler not set up." ) ; } } public sendDistributionMetric ( name , value , forceAsync , ... tags ) { this . sendDistributionMetricWithDate ( name , value , new Date ( Date . now ( ) ) , forceAsync , ... tags ) ; } private async createProcessor ( config , apiKey ) { const key = await apiKey ; const url = `https://api.${ config . siteURL }` ; const apiClient = new APIClient ( key , url ) ; const processor = new Processor ( apiClient , metricsBatchSendIntervalMS , config . shouldRetryMetrics ) ; processor . startProcessing ( ) ; return processor ; } private async getAPIKey ( config ) { if ( config . apiKey !== "" ) { return config . apiKey ; } if ( config . apiKeyKMS !== "" ) { try { return await this . kmsClient . decrypt ( config . apiKeyKMS ) ; } catch ( error ) { if ( error instanceof Error ) { logError ( "couldn't decrypt kms api key" , error as Error ) ; } } } else { const errorMessage = "api key not configured, see https://dtdg.co/sls-node-metrics" ; if ( config . logForwarding ) { logDebug ( errorMessage ) ; } else { logError ( errorMessage ) ; } } return "" ; } }