-
Notifications
You must be signed in to change notification settings - Fork 769
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
src/goLogging: extend logging facility
so it can be used to output to vscode.OutputChannel Change-Id: Ib84eb56c65aeb7e81f14ad1fea7f72d6f72c9219 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/310749 Trust: Hyang-Ah Hana Kim <hyangah@gmail.com> Trust: Suzy Mueller <suzmue@golang.org> Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com> TryBot-Result: kokoro <noreply+kokoro@google.com> Reviewed-by: Suzy Mueller <suzmue@golang.org>
- Loading branch information
Showing
2 changed files
with
117 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,104 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/*--------------------------------------------------------- | ||
* Copyright 2020 The Go Authors. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE in the project root for license information. | ||
*--------------------------------------------------------*/ | ||
|
||
'use strict'; | ||
|
||
// Our log level. | ||
enum LogLevel { | ||
Off = 100, | ||
Error = 50, | ||
Info = 30, | ||
Verbose = 20 | ||
// TODO: Trace, Warn level | ||
} | ||
|
||
let currentLogLevel: LogLevel = LogLevel.Error; | ||
type LogLevel = 'off' | 'error' | 'info' | 'trace' | 'verbose'; | ||
|
||
const levelMap: { [k: string]: LogLevel } = { | ||
off: LogLevel.Off, | ||
error: LogLevel.Error, | ||
info: LogLevel.Info, | ||
verbose: LogLevel.Verbose | ||
const levels: { [key in LogLevel]: number } = { | ||
off: -1, | ||
error: 0, | ||
info: 1, | ||
trace: 2, | ||
verbose: 3 | ||
}; | ||
// TODO: consider 'warning' level. | ||
|
||
function levelPrefix(l: LogLevel): string { | ||
switch (l) { | ||
case LogLevel.Off: | ||
return 'Go[O]:'; | ||
case LogLevel.Error: | ||
return 'Go[E]:'; | ||
case LogLevel.Info: | ||
return 'Go[I]:'; | ||
case LogLevel.Verbose: | ||
return 'Go[V]:'; | ||
default: | ||
return 'Go[?]:'; | ||
function levelToString(level: number) { | ||
switch (level) { | ||
case levels.error: | ||
return 'Error'; | ||
case levels.info: | ||
return 'Info'; | ||
case levels.trace: | ||
return 'Trace'; | ||
case levels.verbose: | ||
return 'Verbose'; | ||
} | ||
return ''; | ||
} | ||
|
||
export interface LogConfig { | ||
level: string; | ||
interface outputChannelType { | ||
appendLine: (msg: string) => void; | ||
} | ||
// Logger outputs messages of the specified log levels to the vscode output channel or console. | ||
export class Logger { | ||
protected minLevel: number; | ||
|
||
export function setLogConfig(cfg: LogConfig) { | ||
const logLevel = cfg?.level || 'error'; | ||
const l = levelMap[logLevel]; | ||
if (l) { | ||
currentLogLevel = l; | ||
return; | ||
constructor(levelName: LogLevel, private outputChannel?: outputChannelType, private logToConsole?: boolean) { | ||
this.minLevel = levels[levelName] || levels.error; | ||
} | ||
|
||
protected log(msgLevel: number, msg: string) { | ||
if (this.minLevel < 0) { | ||
return; // logging is off. | ||
} | ||
if (this.minLevel < msgLevel) { | ||
return; | ||
} | ||
this.outputChannel?.appendLine(msg); | ||
if (this.logToConsole) console.log(msg); | ||
} | ||
|
||
error(msg: string) { | ||
this.log(levels.error, msg); | ||
} | ||
info(msg: string) { | ||
this.log(levels.info, msg); | ||
} | ||
trace(msg: string) { | ||
this.log(levels.trace, msg); | ||
} | ||
debug(msg: string) { | ||
this.log(levels.verbose, msg); | ||
} | ||
logError(`setLogLevel requested with invalid log level ${logLevel}, ignoring...`); | ||
} | ||
|
||
// tslint:disable-next-line:no-any | ||
function log(logLevel: LogLevel, ...args: any[]) { | ||
if (logLevel < currentLogLevel) { | ||
return; | ||
// TimestampedLogger is a logger that prepends the timestamp to every log message. | ||
export class TimestampedLogger extends Logger { | ||
log(msgLevel: number, msg: string) { | ||
const ts = new Date(); | ||
const hhmmss = ts.toLocaleTimeString([], { | ||
hour: '2-digit', | ||
minute: '2-digit', | ||
second: '2-digit', | ||
hour12: false | ||
}); | ||
const msec = ts.getMilliseconds(); | ||
super.log(msgLevel, `[${levelToString(msgLevel)} - ${hhmmss}.${msec}] ${msg}`); | ||
} | ||
const p = levelPrefix(logLevel); | ||
const a = Array.from(args); | ||
a.unshift(p); | ||
console.log(...a); | ||
// TODO: support logging in vscode output channel. | ||
} | ||
|
||
// tslint:disable-next-line:no-any | ||
export function logVerbose(...args: any[]) { | ||
log(LogLevel.Verbose, ...args); | ||
export interface LogConfig { | ||
level: LogLevel; | ||
} | ||
|
||
let defaultLogger: Logger; | ||
|
||
export function setLogConfig(cfg: LogConfig) { | ||
defaultLogger = new Logger(cfg.level); | ||
} | ||
|
||
export function logVerbose(msg: string) { | ||
defaultLogger?.debug(msg); | ||
} | ||
|
||
// tslint:disable-next-line:no-any | ||
export function logError(...args: any[]) { | ||
log(LogLevel.Error, ...args); | ||
export function logError(msg: string) { | ||
defaultLogger?.error(msg); | ||
} | ||
|
||
// tslint:disable-next-line:no-any | ||
export function logInfo(...args: any[]) { | ||
log(LogLevel.Info, ...args); | ||
export function logInfo(msg: string) { | ||
defaultLogger?.info(msg); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/*--------------------------------------------------------- | ||
* Copyright 2021 The Go Authors. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE in the project root for license information. | ||
*--------------------------------------------------------*/ | ||
|
||
import * as assert from 'assert'; | ||
import sinon = require('sinon'); | ||
import { Logger } from '../../src/goLogging'; | ||
|
||
suite('Logger Tests', () => { | ||
let sandbox: sinon.SinonSandbox; | ||
|
||
setup(() => { | ||
sandbox = sinon.createSandbox(); | ||
}); | ||
teardown(() => { | ||
sandbox.restore(); | ||
}); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function runTest(level: any, want: number) { | ||
const appendLine = sandbox.fake(); | ||
const logger = new Logger(level, { appendLine }); | ||
logger.error('error'); | ||
logger.info('info'); | ||
logger.debug('debug'); | ||
logger.trace('trace'); | ||
assert.strictEqual(appendLine.callCount, want, `called ${appendLine.callCount} times, want ${want}`); | ||
} | ||
test('logger level = off', () => runTest('off', 0)); | ||
test('logger level = error', () => runTest('error', 1)); | ||
test('logger level = info', () => runTest('info', 2)); | ||
test('logger level = trace', () => runTest('trace', 3)); | ||
test('logger level = verbose', () => runTest('verbose', 4)); | ||
test('logger level = undefined', () => runTest(undefined, 1)); | ||
test('logger level = ""', () => runTest('', 1)); | ||
test('logger level = object', () => runTest({}, 1)); | ||
test('logger level = number', () => runTest(10, 1)); | ||
}); |