Skip to content

Commit

Permalink
src/goLogging: extend logging facility
Browse files Browse the repository at this point in the history
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
hyangah committed Apr 19, 2021
1 parent afa59f6 commit 8755d84
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 55 deletions.
133 changes: 78 additions & 55 deletions src/goLogging.ts
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);
}
39 changes: 39 additions & 0 deletions test/unit/logger.test.ts
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));
});

0 comments on commit 8755d84

Please sign in to comment.