'use strict' ; import { ExtensionContext , OutputChannel , Uri , window } from 'vscode' ; import { extensionOutputChannelName } from './constants' ; import { getCorrelationContext } from './system' ; const emptyStr = '' ; export enum TraceLevel { Silent = 'silent' , Errors = 'errors' , Verbose = 'verbose' , Debug = 'debug' } const ConsolePrefix = `[${ extensionOutputChannelName }]` ; const isDebuggingRegex = /\remotehub\b/i ; export interface LogCorrelationContext { readonly correlationId ? : number ; readonly prefix : string ; exitDetails ? : string ; } export class Logger { static output : OutputChannel | undefined ; static customLoggableFn : ( ( o : object ) => string | undefined ) | undefined ; static configure ( context , level , loggableFn ? ) { this . customLoggableFn = loggableFn ; this . level = level ; } private static _level : TraceLevel = TraceLevel . Silent ; static get level ( ) { return this . _level ; } static set level ( value ) { this . _level = value ; if ( value === TraceLevel . Silent ) { if ( this . output !== undefined ) { this . output . dispose ( ) ; this . output = undefined ; } } else { this . output = this . output || window . createOutputChannel ( extensionOutputChannelName ) ; } } static debug ( message , ... params ) ; static debug ( context , message , ... params ) ; static debug ( contextOrMessage , ... params ) { if ( this . level !== TraceLevel . Debug && ! Logger . isDebugging ) return ; let message ; if ( typeof contextOrMessage === 'string' ) { message = contextOrMessage ; } else { message = params . shift ( ) ; if ( contextOrMessage !== undefined ) { message = `${ contextOrMessage . prefix } ${ message || emptyStr }` ; } } if ( Logger . isDebugging ) { console . log ( this . timestamp , ConsolePrefix , message || emptyStr , ... params ) ; } if ( this . output !== undefined && this . level === TraceLevel . Debug ) { this . output . appendLine ( `${ this . timestamp } ${ message || emptyStr }${ this . toLoggableParams ( true , params ) }` ) ; } } static error ( ex , message ? , ... params ) ; static error ( ex , context ? , message ? , ... params ) ; static error ( ex , contextOrMessage , ... params ) { if ( this . level === TraceLevel . Silent && ! Logger . isDebugging ) return ; let message ; if ( contextOrMessage === undefined || typeof contextOrMessage === 'string' ) { message = contextOrMessage ; } else { message = params . shift ( ) ; if ( contextOrMessage !== undefined ) { message = `${ contextOrMessage . prefix } ${ message || emptyStr }` ; } } if ( message === undefined ) { const stack = ex . stack ; if ( stack ) { const match = /.*\s*?at\s(.+?)\s/ . exec ( stack ) ; if ( match != null ) { message = match [ 1 ] ; } } } if ( Logger . isDebugging ) { console . error ( this . timestamp , ConsolePrefix , message || emptyStr , ... params , ex ) ; } if ( this . output !== undefined && this . level !== TraceLevel . Silent ) { this . output . appendLine ( `${ this . timestamp } ${ message || emptyStr }${ this . toLoggableParams ( false , params ) }\n${ ex }` ) ; } } static getCorrelationContext ( ) { return getCorrelationContext ( ) ; } static log ( message , ... params ) ; static log ( context , message , ... params ) ; static log ( contextOrMessage , ... params ) { if ( this . level !== TraceLevel . Verbose && this . level !== TraceLevel . Debug && ! Logger . isDebugging ) { return ; } let message ; if ( typeof contextOrMessage === 'string' ) { message = contextOrMessage ; } else { message = params . shift ( ) ; if ( contextOrMessage !== undefined ) { message = `${ contextOrMessage . prefix } ${ message || emptyStr }` ; } } if ( Logger . isDebugging ) { console . log ( this . timestamp , ConsolePrefix , message || emptyStr , ... params ) ; } if ( this . output !== undefined && ( this . level === TraceLevel . Verbose || this . level === TraceLevel . Debug ) ) { this . output . appendLine ( `${ this . timestamp } ${ message || emptyStr }${ this . toLoggableParams ( false , params ) }` ) ; } } static logWithDebugParams ( message , ... params ) ; static logWithDebugParams ( context , message , ... params ) ; static logWithDebugParams ( contextOrMessage , ... params ) { if ( this . level !== TraceLevel . Verbose && this . level !== TraceLevel . Debug && ! Logger . isDebugging ) { return ; } let message ; if ( typeof contextOrMessage === 'string' ) { message = contextOrMessage ; } else { message = params . shift ( ) ; if ( contextOrMessage !== undefined ) { message = `${ contextOrMessage . prefix } ${ message || emptyStr }` ; } } if ( Logger . isDebugging ) { console . log ( this . timestamp , ConsolePrefix , message || emptyStr , ... params ) ; } if ( this . output !== undefined && ( this . level === TraceLevel . Verbose || this . level === TraceLevel . Debug ) ) { this . output . appendLine ( `${ this . timestamp } ${ message || emptyStr }${ this . toLoggableParams ( true , params ) }` ) ; } } static warn ( message , ... params ) ; static warn ( context , message , ... params ) ; static warn ( contextOrMessage , ... params ) { if ( this . level === TraceLevel . Silent && ! Logger . isDebugging ) return ; let message ; if ( typeof contextOrMessage === 'string' ) { message = contextOrMessage ; } else { message = params . shift ( ) ; if ( contextOrMessage !== undefined ) { message = `${ contextOrMessage . prefix } ${ message || emptyStr }` ; } } if ( Logger . isDebugging ) { console . warn ( this . timestamp , ConsolePrefix , message || emptyStr , ... params ) ; } if ( this . output !== undefined && this . level !== TraceLevel . Silent ) { this . output . appendLine ( `${ this . timestamp } ${ message || emptyStr }${ this . toLoggableParams ( false , params ) }` ) ; } } static showOutputChannel ( ) { if ( this . output === undefined ) return ; this . output . show ( ) ; } static toLoggable ( p , sanitize ? ) { if ( typeof p !== 'object' ) return String ( p ) ; if ( this . customLoggableFn !== undefined ) { const loggable = this . customLoggableFn ( p ) ; if ( loggable != null ) return loggable ; } if ( p instanceof Uri ) return `Uri(${ p . toString ( true ) })` ; try { return JSON . stringify ( p , sanitize ) ; } catch { return '<error>' ; } } static toLoggableName ( instance ) { let name ; if ( typeof instance === 'function' ) { if ( instance . prototype == null || instance . prototype . constructor == null ) { return instance . name ; } name = instance . prototype . constructor . name ; } else { name = instance . constructor != null ? instance . constructor . name : emptyStr ; } const index = name . indexOf ( '_' ) ; return index === - 1 ? name : name . substr ( index + 1 ) ; } private static get timestamp ( ) : string { const now = new Date ( ) ; return `[${ now . toISOString ( ) . replace ( /T/ , ' ' ) . replace ( /\..+/ , emptyStr ) }:${ `00${ now . getUTCMilliseconds ( ) }` . slice ( - 3 ) }]` ; } private static toLoggableParams ( debugOnly , params ) { if ( params . length === 0 || ( debugOnly && this . level !== TraceLevel . Debug && ! Logger . isDebugging ) ) { return emptyStr ; } const loggableParams = params . map ( p => this . toLoggable ( p ) ) . join ( ', ' ) ; return loggableParams . length !== 0 ? ` \u2014 ${ loggableParams }` : emptyStr ; } private static _isDebugging : boolean | undefined ; static get isDebugging ( ) { if ( this . _isDebugging === undefined ) { const env = process . env ; this . _isDebugging = env && env . VSCODE_DEBUGGING_EXTENSION ? isDebuggingRegex . test ( env . VSCODE_DEBUGGING_EXTENSION ) : false ; } return this . _isDebugging ; } }