@ -1,27 +1,7 @@
import type { ExtensionContext , OutputChannel } from 'vscode' ;
import { ExtensionMode , Uri , window } from 'vscode' ;
import { OutputLevel } from './configuration' ;
import { LogLevel } from './constants' ;
import type { LogScope } from './logScope' ;
const emptyStr = '' ;
const outputChannelName = 'GitLens' ;
const consolePrefix = '[GitLens]' ;
const gitOutputChannelName = 'GitLens (Git)' ;
const gitConsolePrefix = '[GitLens (Git)]' ;
export const enum LogLevel {
Off = 'off' ,
Error = 'error' ,
Warn = 'warn' ,
Info = 'info' ,
Debug = 'debug' ,
}
export interface LogScope {
readonly scopeId? : number ;
readonly prefix : string ;
exitDetails? : string ;
}
const enum OrderedLevel {
Off = 0 ,
@ -31,48 +11,63 @@ const enum OrderedLevel {
Debug = 4 ,
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class Logger {
static readonly slowCallWarningThreshold = 500 ;
export interface LogChannelProvider {
readonly name : string ;
createChannel ( name : string ) : LogChannel ;
toLoggable ? ( o : unknown ) : string | undefined ;
}
private static output : OutputChannel | undefined ;
private static customLoggableFn : ( ( o : object ) = > string | undefined ) | undefined ;
export interface LogChannel {
readonly name : string ;
appendLine ( value : string ) : void ;
dispose ( ) : void ;
show ( preserveFocus? : boolean ) : void ;
}
static configure ( context : ExtensionContext , outputLevel : OutputLevel , loggableFn ? : ( o : any ) = > string | undefined ) {
this . _isDebugging = context . extensionMode === ExtensionMode . Development ;
this . logLevel = outputLevel ;
this . customLoggableFn = loggableFn ;
export const Logger = new ( class Logger {
private output : LogChannel | undefined ;
private provider : LogChannelProvider | undefined ;
configure ( provider : LogChannelProvider , logLevel : LogLevel , debugging : boolean = false ) {
this . provider = provider ;
this . _isDebugging = debugging ;
this . logLevel = logLevel ;
}
static enabled ( level : LogLevel ) : boolean {
enabled ( level : LogLevel ) : boolean {
return this . level >= toOrderedLevel ( level ) ;
}
private static _isDebugging : boolean ;
static get isDebugging() {
private _isDebugging = false ;
get isDebugging() {
return this . _isDebugging ;
}
private static level : OrderedLevel = OrderedLevel . Off ;
private static _logLevel : LogLevel = LogLevel . Off ;
static get logLevel ( ) : LogLevel {
private level : OrderedLevel = OrderedLevel . Off ;
private _logLevel : LogLevel = LogLevel . Off ;
get logLevel ( ) : LogLevel {
return this . _logLevel ;
}
static s et logLevel ( value : LogLevel | Output Level ) {
this . _logLevel = fromOutputLevel ( value) ;
set logLevel ( value : LogLevel ) {
this . _logLevel = value ;
this . level = toOrderedLevel ( this . _logLevel ) ;
if ( value === LogLevel . Off ) {
this . output ? . dispose ( ) ;
this . output = undefined ;
} else {
this . output = this . output ? ? window . createOutput Channel ( outputChannelN ame) ;
this . output = this . output ? ? this . provider ! . createChannel ( this . provider ! . n ame) ;
}
}
static debug ( message : string , . . . params : any [ ] ) : void ;
static debug ( scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
static debug ( scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
get timestamp ( ) : string {
return ` [ ${ new Date ( ) . toISOString ( ) . replace ( /T/ , ' ' ) . slice ( 0 , - 1 ) } ] ` ;
}
debug ( message : string , . . . params : any [ ] ) : void ;
debug ( scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
debug ( scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
if ( this . level < OrderedLevel . Debug && ! this . isDebugging ) return ;
let message ;
@ -87,16 +82,16 @@ export class Logger {
}
if ( this . isDebugging ) {
console . log ( this . timestamp , consolePrefix , message ? ? emptyStr , . . . params ) ;
console . log ( this . timestamp , ` [ ${ this . provider ! . name } ] ` , message ? ? emptyStr , . . . params ) ;
}
if ( this . output == null || this . level < OrderedLevel . Debug ) return ;
this . output . appendLine ( ` ${ this . timestamp } ${ message ? ? emptyStr } ${ this . toLoggableParams ( true , params ) } ` ) ;
}
static error ( ex : Error | unknown , message? : string , . . . params : any [ ] ) : void ;
static error ( ex : Error | unknown , scope? : LogScope , message? : string , . . . params : any [ ] ) : void ;
static error ( ex : Error | unknown , scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
error ( ex : Error | unknown , message? : string , . . . params : any [ ] ) : void ;
error ( ex : Error | unknown , scope? : LogScope , message? : string , . . . params : any [ ] ) : void ;
error ( ex : Error | unknown , scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
if ( this . level < OrderedLevel . Error && ! this . isDebugging ) return ;
let message ;
@ -117,7 +112,7 @@ export class Logger {
}
if ( this . isDebugging ) {
console . error ( this . timestamp , consolePrefix , message ? ? emptyStr , . . . params , ex ) ;
console . error ( this . timestamp , ` [ ${ this . provider ! . name } ] ` , message ? ? emptyStr , . . . params , ex ) ;
}
if ( this . output == null || this . level < OrderedLevel . Error ) return ;
@ -126,9 +121,9 @@ export class Logger {
) ;
}
static log ( message : string , . . . params : any [ ] ) : void ;
static log ( scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
static log ( scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
log ( message : string , . . . params : any [ ] ) : void ;
log ( scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
log ( scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
if ( this . level < OrderedLevel . Info && ! this . isDebugging ) return ;
let message ;
@ -143,16 +138,16 @@ export class Logger {
}
if ( this . isDebugging ) {
console . log ( this . timestamp , consolePrefix , message ? ? emptyStr , . . . params ) ;
console . log ( this . timestamp , ` [ ${ this . provider ! . name } ] ` , message ? ? emptyStr , . . . params ) ;
}
if ( this . output == null || this . level < OrderedLevel . Info ) return ;
this . output . appendLine ( ` ${ this . timestamp } ${ message ? ? emptyStr } ${ this . toLoggableParams ( false , params ) } ` ) ;
}
static warn ( message : string , . . . params : any [ ] ) : void ;
static warn ( scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
static warn ( scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
warn ( message : string , . . . params : any [ ] ) : void ;
warn ( scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
warn ( scopeOrMessage : LogScope | string | undefined , . . . params : any [ ] ) : void {
if ( this . level < OrderedLevel . Warn && ! this . isDebugging ) return ;
let message ;
@ -167,37 +162,31 @@ export class Logger {
}
if ( this . isDebugging ) {
console . warn ( this . timestamp , consolePrefix , message ? ? emptyStr , . . . params ) ;
console . warn ( this . timestamp , ` [ ${ this . provider ! . name } ] ` , message ? ? emptyStr , . . . params ) ;
}
if ( this . output == null || this . level < OrderedLevel . Warn ) return ;
this . output . appendLine ( ` ${ this . timestamp } ${ message ? ? emptyStr } ${ this . toLoggableParams ( false , params ) } ` ) ;
}
static showOutputChannel ( ) : void {
this . output ? . show ( ) ;
showOutputChannel ( preserveFocus? : boolean ) : void {
this . output ? . show ( preserveFocus ) ;
}
static toLoggable ( p : any , sanitize ? : ( ( key : string , value : any ) = > any ) | undefined ) {
if ( typeof p !== 'object' ) return String ( p ) ;
if ( this . customLoggableFn != null ) {
const loggable = this . customLoggableFn ( p ) ;
if ( loggable != null ) return loggable ;
}
if ( p instanceof Uri ) return ` Uri( ${ p . toString ( true ) } ) ` ;
toLoggable ( o : any , sanitize ? : ( ( key : string , value : any ) = > any ) | undefined ) {
if ( typeof o !== 'object' ) return String ( o ) ;
const loggable = this . provider ! . toLoggable ? . ( o ) ;
if ( loggable != null ) return loggable ;
try {
return JSON . stringify ( p , sanitize ) ;
return JSON . stringify ( o , sanitize ) ;
} catch {
return '<error>' ;
}
}
private static get timestamp ( ) : string {
return ` [ ${ new Date ( ) . toISOString ( ) . replace ( /T/ , ' ' ) . slice ( 0 , - 1 ) } ] ` ;
}
private static toLoggableParams ( debugOnly : boolean , params : any [ ] ) {
private toLoggableParams ( debugOnly : boolean , params : any [ ] ) {
if ( params . length === 0 || ( debugOnly && this . level < OrderedLevel . Debug && ! this . isDebugging ) ) {
return emptyStr ;
}
@ -205,49 +194,7 @@ export class Logger {
const loggableParams = params . map ( p = > this . toLoggable ( p ) ) . join ( ', ' ) ;
return loggableParams . length !== 0 ? ` \ u2014 ${ loggableParams } ` : emptyStr ;
}
static gitOutput : OutputChannel | undefined ;
static logGitCommand ( command : string , duration : number , ex? : Error ) : void {
if ( this . level < OrderedLevel . Debug && ! this . isDebugging ) return ;
const slow = duration > Logger . slowCallWarningThreshold ;
if ( this . isDebugging ) {
if ( ex != null ) {
console . error ( this . timestamp , gitConsolePrefix , command ? ? emptyStr , ex ) ;
} else if ( slow ) {
console . warn ( this . timestamp , gitConsolePrefix , command ? ? emptyStr ) ;
} else {
console . log ( this . timestamp , gitConsolePrefix , command ? ? emptyStr ) ;
}
}
if ( this . gitOutput == null ) {
this . gitOutput = window . createOutputChannel ( gitOutputChannelName ) ;
}
this . gitOutput . appendLine (
` ${ this . timestamp } [ ${ slow ? '*' : ' ' } ${ duration . toString ( ) . padStart ( 6 ) } ms] ${ command } ${
ex != null ? ` \ n \ n ${ ex . toString ( ) } ` : emptyStr
} ` ,
) ;
}
}
function fromOutputLevel ( level : LogLevel | OutputLevel ) : LogLevel {
switch ( level ) {
case OutputLevel . Silent :
return LogLevel . Off ;
case OutputLevel . Errors :
return LogLevel . Error ;
case OutputLevel . Verbose :
return LogLevel . Info ;
case OutputLevel . Debug :
return LogLevel . Debug ;
default :
return level ;
}
}
} ) ( ) ;
function toOrderedLevel ( logLevel : LogLevel ) : OrderedLevel {
switch ( logLevel ) {
@ -280,3 +227,28 @@ export function getLoggableName(instance: Function | object) {
const index = name . indexOf ( '_' ) ;
return index === - 1 ? name : name.substr ( index + 1 ) ;
}
export interface LogProvider {
enabled ( logLevel : LogLevel ) : boolean ;
log ( logLevel : LogLevel , scope : LogScope | undefined , message : string , . . . params : any [ ] ) : void ;
}
export const defaultLogProvider : LogProvider = {
enabled : ( logLevel : LogLevel ) = > Logger . enabled ( logLevel ) ,
log : ( logLevel : LogLevel , scope : LogScope | undefined , message : string , . . . params : any [ ] ) = > {
switch ( logLevel ) {
case LogLevel . Error :
Logger . error ( '' , scope , message , . . . params ) ;
break ;
case LogLevel . Warn :
Logger . warn ( scope , message , . . . params ) ;
break ;
case LogLevel . Info :
Logger . log ( scope , message , . . . params ) ;
break ;
default :
Logger . debug ( scope , message , . . . params ) ;
break ;
}
} ,
} ;