|
@ -1,16 +1,30 @@ |
|
|
'use strict'; |
|
|
'use strict'; |
|
|
import { ExtensionContext, ExtensionMode, OutputChannel, Uri, window } from 'vscode'; |
|
|
import { ExtensionContext, ExtensionMode, OutputChannel, Uri, window } from 'vscode'; |
|
|
import { TraceLevel } from './configuration'; |
|
|
|
|
|
|
|
|
import { OutputLevel } from './configuration'; |
|
|
import { getCorrelationContext, getNextCorrelationId } from './system'; |
|
|
import { getCorrelationContext, getNextCorrelationId } from './system'; |
|
|
|
|
|
|
|
|
const emptyStr = ''; |
|
|
const emptyStr = ''; |
|
|
const extensionOutputChannelName = 'GitLens'; |
|
|
|
|
|
const ConsolePrefix = '[GitLens]'; |
|
|
|
|
|
|
|
|
|
|
|
const extensionGitOutputChannelName = 'GitLens (Git)'; |
|
|
|
|
|
const GitConsolePrefix = '[GitLens (Git)]'; |
|
|
|
|
|
|
|
|
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 { TraceLevel } from './configuration'; |
|
|
|
|
|
|
|
|
const enum OrderedLevel { |
|
|
|
|
|
Off = 0, |
|
|
|
|
|
Error = 1, |
|
|
|
|
|
Warn = 2, |
|
|
|
|
|
Info = 3, |
|
|
|
|
|
Debug = 4, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
export interface LogCorrelationContext { |
|
|
export interface LogCorrelationContext { |
|
|
readonly correlationId?: number; |
|
|
readonly correlationId?: number; |
|
@ -19,14 +33,17 @@ export interface LogCorrelationContext { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export class Logger { |
|
|
export class Logger { |
|
|
static output: OutputChannel | undefined; |
|
|
|
|
|
static customLoggableFn: ((o: object) => string | undefined) | undefined; |
|
|
|
|
|
|
|
|
private static output: OutputChannel | undefined; |
|
|
|
|
|
private static customLoggableFn: ((o: object) => string | undefined) | undefined; |
|
|
|
|
|
|
|
|
static configure(context: ExtensionContext, level: TraceLevel, loggableFn?: (o: any) => string | undefined) { |
|
|
|
|
|
|
|
|
static configure(context: ExtensionContext, outputLevel: OutputLevel, loggableFn?: (o: any) => string | undefined) { |
|
|
|
|
|
this._isDebugging = context.extensionMode === ExtensionMode.Development; |
|
|
|
|
|
this.logLevel = outputLevel; |
|
|
this.customLoggableFn = loggableFn; |
|
|
this.customLoggableFn = loggableFn; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
this._isDebugging = context.extensionMode === ExtensionMode.Development; |
|
|
|
|
|
this.level = level; |
|
|
|
|
|
|
|
|
static enabled(level: LogLevel): boolean { |
|
|
|
|
|
return this.level >= toOrderedLevel(level); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static _isDebugging: boolean; |
|
|
private static _isDebugging: boolean; |
|
@ -34,26 +51,27 @@ export class Logger { |
|
|
return this._isDebugging; |
|
|
return this._isDebugging; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static _level: TraceLevel = TraceLevel.Silent; |
|
|
|
|
|
static get level() { |
|
|
|
|
|
return this._level; |
|
|
|
|
|
|
|
|
private static level: OrderedLevel = OrderedLevel.Off; |
|
|
|
|
|
private static _logLevel: LogLevel = LogLevel.Off; |
|
|
|
|
|
static get logLevel(): LogLevel { |
|
|
|
|
|
return this._logLevel; |
|
|
} |
|
|
} |
|
|
static set level(value: TraceLevel) { |
|
|
|
|
|
this._level = value; |
|
|
|
|
|
if (value === TraceLevel.Silent) { |
|
|
|
|
|
if (this.output != null) { |
|
|
|
|
|
this.output.dispose(); |
|
|
|
|
|
this.output = undefined; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static set logLevel(value: LogLevel | OutputLevel) { |
|
|
|
|
|
this._logLevel = fromOutputLevel(value); |
|
|
|
|
|
this.level = toOrderedLevel(this._logLevel); |
|
|
|
|
|
|
|
|
|
|
|
if (value === LogLevel.Off) { |
|
|
|
|
|
this.output?.dispose(); |
|
|
|
|
|
this.output = undefined; |
|
|
} else { |
|
|
} else { |
|
|
this.output = this.output ?? window.createOutputChannel(extensionOutputChannelName); |
|
|
|
|
|
|
|
|
this.output = this.output ?? window.createOutputChannel(outputChannelName); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static debug(message: string, ...params: any[]): void; |
|
|
static debug(message: string, ...params: any[]): void; |
|
|
static debug(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static debug(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static debug(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
static debug(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
if (this.level !== TraceLevel.Debug && !Logger.isDebugging) return; |
|
|
|
|
|
|
|
|
if (this.level < OrderedLevel.Debug && !this.isDebugging) return; |
|
|
|
|
|
|
|
|
let message; |
|
|
let message; |
|
|
if (typeof contextOrMessage === 'string') { |
|
|
if (typeof contextOrMessage === 'string') { |
|
@ -66,19 +84,22 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (Logger.isDebugging) { |
|
|
|
|
|
console.log(this.timestamp, ConsolePrefix, message ?? emptyStr, ...params); |
|
|
|
|
|
|
|
|
if (this.isDebugging) { |
|
|
|
|
|
console.log(this.timestamp, consolePrefix, message ?? emptyStr, ...params); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.output != null && this.level === TraceLevel.Debug) { |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(true, 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, message?: string, ...params: any[]): void; |
|
|
|
|
|
static error(ex: Error, context?: LogCorrelationContext, message?: string, ...params: any[]): void; |
|
|
|
|
|
static error(ex: Error, contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
|
|
|
if (this.level === TraceLevel.Silent && !Logger.isDebugging) return; |
|
|
|
|
|
|
|
|
static error(ex: Error | unknown, message?: string, ...params: any[]): void; |
|
|
|
|
|
static error(ex: Error | unknown, context?: LogCorrelationContext, message?: string, ...params: any[]): void; |
|
|
|
|
|
static error( |
|
|
|
|
|
ex: Error | unknown, |
|
|
|
|
|
contextOrMessage: LogCorrelationContext | string | undefined, |
|
|
|
|
|
...params: any[] |
|
|
|
|
|
): void { |
|
|
|
|
|
if (this.level < OrderedLevel.Error && !this.isDebugging) return; |
|
|
|
|
|
|
|
|
let message; |
|
|
let message; |
|
|
if (contextOrMessage == null || typeof contextOrMessage === 'string') { |
|
|
if (contextOrMessage == null || typeof contextOrMessage === 'string') { |
|
@ -88,7 +109,7 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (message == null) { |
|
|
if (message == null) { |
|
|
const stack = ex.stack; |
|
|
|
|
|
|
|
|
const stack = ex instanceof Error ? ex.stack : undefined; |
|
|
if (stack) { |
|
|
if (stack) { |
|
|
const match = /.*\s*?at\s(.+?)\s/.exec(stack); |
|
|
const match = /.*\s*?at\s(.+?)\s/.exec(stack); |
|
|
if (match != null) { |
|
|
if (match != null) { |
|
@ -97,35 +118,20 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (Logger.isDebugging) { |
|
|
|
|
|
console.error(this.timestamp, ConsolePrefix, message ?? emptyStr, ...params, ex); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (this.output != null && this.level !== TraceLevel.Silent) { |
|
|
|
|
|
this.output.appendLine( |
|
|
|
|
|
`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(false, params)}\n${ex?.toString()}`, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
if (this.isDebugging) { |
|
|
|
|
|
console.error(this.timestamp, consolePrefix, message ?? emptyStr, ...params, ex); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static getCorrelationContext() { |
|
|
|
|
|
return getCorrelationContext(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static getNewCorrelationContext(prefix: string): LogCorrelationContext { |
|
|
|
|
|
const correlationId = getNextCorrelationId(); |
|
|
|
|
|
return { |
|
|
|
|
|
correlationId: correlationId, |
|
|
|
|
|
prefix: `[${correlationId}] ${prefix}`, |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
if (this.output == null || this.level < OrderedLevel.Error) return; |
|
|
|
|
|
this.output.appendLine( |
|
|
|
|
|
`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(false, params)}\n${String(ex)}`, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static log(message: string, ...params: any[]): void; |
|
|
static log(message: string, ...params: any[]): void; |
|
|
static log(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static log(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static log(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
static log(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
if (this.level !== TraceLevel.Verbose && this.level !== TraceLevel.Debug && !Logger.isDebugging) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this.level < OrderedLevel.Info && !this.isDebugging) return; |
|
|
|
|
|
|
|
|
let message; |
|
|
let message; |
|
|
if (typeof contextOrMessage === 'string') { |
|
|
if (typeof contextOrMessage === 'string') { |
|
@ -138,21 +144,18 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (Logger.isDebugging) { |
|
|
|
|
|
console.log(this.timestamp, ConsolePrefix, message ?? emptyStr, ...params); |
|
|
|
|
|
|
|
|
if (this.isDebugging) { |
|
|
|
|
|
console.log(this.timestamp, consolePrefix, message ?? emptyStr, ...params); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.output != null && (this.level === TraceLevel.Verbose || this.level === TraceLevel.Debug)) { |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(false, params)}`); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this.output == null || this.level < OrderedLevel.Info) return; |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(false, params)}`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static logWithDebugParams(message: string, ...params: any[]): void; |
|
|
static logWithDebugParams(message: string, ...params: any[]): void; |
|
|
static logWithDebugParams(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static logWithDebugParams(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static logWithDebugParams(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
static logWithDebugParams(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
if (this.level !== TraceLevel.Verbose && this.level !== TraceLevel.Debug && !Logger.isDebugging) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this.level < OrderedLevel.Info && !this.isDebugging) return; |
|
|
|
|
|
|
|
|
let message; |
|
|
let message; |
|
|
if (typeof contextOrMessage === 'string') { |
|
|
if (typeof contextOrMessage === 'string') { |
|
@ -165,19 +168,18 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (Logger.isDebugging) { |
|
|
|
|
|
console.log(this.timestamp, ConsolePrefix, message ?? emptyStr, ...params); |
|
|
|
|
|
|
|
|
if (this.isDebugging) { |
|
|
|
|
|
console.log(this.timestamp, consolePrefix, message ?? emptyStr, ...params); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.output != null && (this.level === TraceLevel.Verbose || this.level === TraceLevel.Debug)) { |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(true, params)}`); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this.output == null || this.level < OrderedLevel.Info) return; |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(true, params)}`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static warn(message: string, ...params: any[]): void; |
|
|
static warn(message: string, ...params: any[]): void; |
|
|
static warn(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static warn(context: LogCorrelationContext | undefined, message: string, ...params: any[]): void; |
|
|
static warn(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
static warn(contextOrMessage: LogCorrelationContext | string | undefined, ...params: any[]): void { |
|
|
if (this.level === TraceLevel.Silent && !Logger.isDebugging) return; |
|
|
|
|
|
|
|
|
if (this.level < OrderedLevel.Warn && !this.isDebugging) return; |
|
|
|
|
|
|
|
|
let message; |
|
|
let message; |
|
|
if (typeof contextOrMessage === 'string') { |
|
|
if (typeof contextOrMessage === 'string') { |
|
@ -190,33 +192,28 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (Logger.isDebugging) { |
|
|
|
|
|
console.warn(this.timestamp, ConsolePrefix, message ?? emptyStr, ...params); |
|
|
|
|
|
|
|
|
if (this.isDebugging) { |
|
|
|
|
|
console.warn(this.timestamp, consolePrefix, message ?? emptyStr, ...params); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.output != null && this.level !== TraceLevel.Silent) { |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(false, params)}`); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (this.output == null || this.level < OrderedLevel.Warn) return; |
|
|
|
|
|
this.output.appendLine(`${this.timestamp} ${message ?? emptyStr}${this.toLoggableParams(false, params)}`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static willLog(type: 'debug' | 'error' | 'log' | 'warn'): boolean { |
|
|
|
|
|
switch (type) { |
|
|
|
|
|
case 'debug': |
|
|
|
|
|
return this.level === TraceLevel.Debug || Logger.isDebugging; |
|
|
|
|
|
case 'error': |
|
|
|
|
|
case 'warn': |
|
|
|
|
|
return this.level !== TraceLevel.Silent || Logger.isDebugging; |
|
|
|
|
|
case 'log': |
|
|
|
|
|
return this.level === TraceLevel.Verbose || this.level === TraceLevel.Debug || Logger.isDebugging; |
|
|
|
|
|
default: |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static getCorrelationContext() { |
|
|
|
|
|
return getCorrelationContext(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static showOutputChannel() { |
|
|
|
|
|
if (this.output == null) return; |
|
|
|
|
|
|
|
|
static getNewCorrelationContext(prefix: string): LogCorrelationContext { |
|
|
|
|
|
const correlationId = getNextCorrelationId(); |
|
|
|
|
|
return { |
|
|
|
|
|
correlationId: correlationId, |
|
|
|
|
|
prefix: `[${String(correlationId).padStart(5)}] ${prefix}`, |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
this.output.show(); |
|
|
|
|
|
|
|
|
static showOutputChannel() { |
|
|
|
|
|
this.output?.show(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static toLoggable(p: any, sanitize?: ((key: string, value: any) => any) | undefined) { |
|
|
static toLoggable(p: any, sanitize?: ((key: string, value: any) => any) | undefined) { |
|
@ -260,7 +257,7 @@ export class Logger { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static toLoggableParams(debugOnly: boolean, params: any[]) { |
|
|
private static toLoggableParams(debugOnly: boolean, params: any[]) { |
|
|
if (params.length === 0 || (debugOnly && this.level !== TraceLevel.Debug && !Logger.isDebugging)) { |
|
|
|
|
|
|
|
|
if (params.length === 0 || (debugOnly && this.level < OrderedLevel.Debug && !this.isDebugging)) { |
|
|
return emptyStr; |
|
|
return emptyStr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -271,19 +268,51 @@ export class Logger { |
|
|
static gitOutput: OutputChannel | undefined; |
|
|
static gitOutput: OutputChannel | undefined; |
|
|
|
|
|
|
|
|
static logGitCommand(command: string, ex?: Error): void { |
|
|
static logGitCommand(command: string, ex?: Error): void { |
|
|
if (this.level !== TraceLevel.Debug) return; |
|
|
|
|
|
|
|
|
if (this.level < OrderedLevel.Debug && !this.isDebugging) return; |
|
|
|
|
|
|
|
|
if (Logger.isDebugging) { |
|
|
|
|
|
|
|
|
if (this.isDebugging) { |
|
|
if (ex != null) { |
|
|
if (ex != null) { |
|
|
console.error(this.timestamp, GitConsolePrefix, command ?? emptyStr, ex); |
|
|
|
|
|
|
|
|
console.error(this.timestamp, gitConsolePrefix, command ?? emptyStr, ex); |
|
|
} else { |
|
|
} else { |
|
|
console.log(this.timestamp, GitConsolePrefix, command ?? emptyStr); |
|
|
|
|
|
|
|
|
console.log(this.timestamp, gitConsolePrefix, command ?? emptyStr); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.gitOutput == null) { |
|
|
if (this.gitOutput == null) { |
|
|
this.gitOutput = window.createOutputChannel(extensionGitOutputChannelName); |
|
|
|
|
|
|
|
|
this.gitOutput = window.createOutputChannel(gitOutputChannelName); |
|
|
} |
|
|
} |
|
|
this.gitOutput.appendLine(`${this.timestamp} ${command}${ex != null ? `\n\n${ex.toString()}` : emptyStr}`); |
|
|
this.gitOutput.appendLine(`${this.timestamp} ${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) { |
|
|
|
|
|
case LogLevel.Off: |
|
|
|
|
|
return OrderedLevel.Off; |
|
|
|
|
|
case LogLevel.Error: |
|
|
|
|
|
return OrderedLevel.Error; |
|
|
|
|
|
case LogLevel.Warn: |
|
|
|
|
|
return OrderedLevel.Warn; |
|
|
|
|
|
case LogLevel.Info: |
|
|
|
|
|
return OrderedLevel.Info; |
|
|
|
|
|
case LogLevel.Debug: |
|
|
|
|
|
return OrderedLevel.Debug; |
|
|
|
|
|
default: |
|
|
|
|
|
return OrderedLevel.Off; |
|
|
|
|
|
} |
|
|
|
|
|
} |