import * as os from 'os' ; import { ConfigAggregator , Logger } from '@salesforce/core' ; import { AsyncCreatable , env } from '@salesforce/kit' ; import axios from 'axios' ; import { AppInsights , Attributes , Properties , TelemetryOptions } from './appInsights' ; import { TelemetryClient } from './exported' ; const DISABLE_TELEMETRY = 'disableTelemetry' ; export { TelemetryOptions , Attributes , Properties , TelemetryClient } from './appInsights' ; /**
 * Reports telemetry events to app insights. We do not send if the config 'disableTelemetry' is set.
 */ export class TelemetryReporter extends AsyncCreatable < TelemetryOptions > { private static config : ConfigAggregator ; private options : TelemetryOptions ; private logger ! : Logger ; private config ! : ConfigAggregator ; private reporter ! : AppInsights ; public constructor ( options ) { super ( options ) ; this . options = options ; } /**
     * Determine if the telemetry event should be logged.
     * Setting the disableTelemetry config var to true will disable insights for errors and diagnostics.
     */ public static async determineSfdxTelemetryEnabled ( ) { if ( ! TelemetryReporter . config ) { TelemetryReporter . config = await ConfigAggregator . create ( { } ) ; } const configValue = TelemetryReporter . config . getPropertyValue ( DISABLE_TELEMETRY ) ; const sfdxDisableInsights = configValue === 'true' || env . getBoolean ( 'SFDX_DISABLE_INSIGHTS' ) ; const isEnabled = ! sfdxDisableInsights ; return isEnabled ; } public async init ( ) { this . logger = await Logger . child ( 'TelemetryReporter' ) ; if ( ! TelemetryReporter . config ) { TelemetryReporter . config = await ConfigAggregator . create ( { } ) ; } this . config = TelemetryReporter . config ; if ( this . options . waitForConnection ) await this . waitForConnection ( ) ; this . reporter = await AppInsights . create ( this . options ) ; } /**
     * Starts data collection services. This is for long running processes. Short lived
     * processes can call send*Event directly then finish it by TelemetryReporter.stop().
     */ public start ( ) { this . reporter . start ( ) ; } /**
     * Immediately flush and dispose of the reporter. This can usually take 1-3 seconds
     * not counting timeouts.
     */ public stop ( ) { this . reporter . stop ( ) ; } public async waitForConnection ( ) { const canConnect = await this . testConnection ( ) ; if ( ! canConnect ) { throw new Error ( 'Unable to connect to app insights.' ) ; } } public async testConnection ( ) { const timeout = parseInt ( env . getString ( 'SFDX_TELEMETRY_TIMEOUT' , '1000' ) , 10 ) ; this . logger . debug ( `Testing connection to ${ AppInsights . APP_INSIGHTS_SERVER } with timeout of ${ timeout } ms` ) ; const cancelRequest = axios . CancelToken . source ( ) ; setTimeout ( ( ) => cancelRequest . cancel ( 'connection timeout' ) , timeout ) ; let canConnect ; try { const options = { timeout , cancelToken : cancelRequest . token , validateStatus : ( status ) : boolean => status < 500 } ; await axios . get ( AppInsights . APP_INSIGHTS_SERVER , options ) ; canConnect = true ; } catch ( err ) { canConnect = false ; } if ( canConnect ) { this . logger . debug ( `Successfully made a connection to ${ AppInsights . APP_INSIGHTS_SERVER }` ) ; } else { this . logger . warn ( `Connection to ${ AppInsights . APP_INSIGHTS_SERVER } timed out after ${ timeout } ms` ) ; } return canConnect ; } param eventName { string } param attributes { Attributes } public sendTelemetryEvent ( eventName , attributes = { } ) { if ( this . isSfdxTelemetryEnabled ( ) ) { this . reporter . sendTelemetryEvent ( eventName , attributes ) ; } } param exception { Error } param measurements { Measurements } public sendTelemetryException ( exception , attributes = { } ) { if ( this . isSfdxTelemetryEnabled ( ) ) { exception . stack = exception . stack && exception . stack . replace ( new RegExp ( os . homedir ( ) , 'g' ) , AppInsights . GDPR_HIDDEN ) ; this . reporter . sendTelemetryException ( exception , attributes ) ; } } param message { string } param properties { Properties } public sendTelemetryTrace ( traceMessage , properties ? ) { if ( this . isSfdxTelemetryEnabled ( ) ) { this . reporter . sendTelemetryTrace ( traceMessage , properties ) ; } } param name { string } param value { number } param properties { Properties } public sendTelemetryMetric ( metricName , value , properties ? ) { if ( this . isSfdxTelemetryEnabled ( ) ) { this . reporter . sendTelemetryMetric ( metricName , value , properties ) ; } } /**
     * Determine if the telemetry event should be logged.
     * Setting the disableTelemetry config var to true will disable insights for errors and diagnostics.
     */ public isSfdxTelemetryEnabled ( ) { const configValue = this . config . getPropertyValue ( DISABLE_TELEMETRY ) ; const sfdxDisableInsights = configValue === 'true' || env . getBoolean ( 'SFDX_DISABLE_INSIGHTS' ) ; const isEnabled = ! sfdxDisableInsights ; return isEnabled ; } public logTelemetryStatus ( ) { const isEnabled = this . isSfdxTelemetryEnabled ( ) ; if ( isEnabled ) { this . logger . warn ( `Telemetry is enabled. This can be disabled by running sfdx force:config:set ${ DISABLE_TELEMETRY }=true` ) ; } else { this . logger . warn ( `Telemetry is disabled. This can be enabled by running sfdx force:config:set ${ DISABLE_TELEMETRY }=false` ) ; } } /**
     * Gets the underline telemetry client. This should only be used to set
     * additional options that are not exposed in the init options. This should
     * NOT be used to send events as it will by pass disabled checks.
     */ public getTelemetryClient ( ) { return this . reporter . appInsightsClient ; } }