2020-04-20 16:52:26 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright Microsoft Corporation. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-04-21 05:00:55 +02:00
|
|
|
import * as debug from 'debug';
|
2020-06-17 02:11:19 +02:00
|
|
|
import { helper } from './helper';
|
|
|
|
|
import { Logger as LoggerSink, LoggerSeverity } from './types';
|
2020-04-20 16:52:26 +02:00
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
export function logError(logger: Logger): (error: Error) => void {
|
|
|
|
|
return error => logger.error(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class Logger {
|
|
|
|
|
private _loggerSink: LoggerSink;
|
|
|
|
|
private _name: string;
|
|
|
|
|
private _hints: { color?: string; };
|
|
|
|
|
private _scopeName: string | undefined;
|
|
|
|
|
private _recording: string[] | undefined;
|
|
|
|
|
|
|
|
|
|
constructor(loggerSink: LoggerSink, name: string, hints: { color?: string }, scopeName?: string, record?: boolean) {
|
|
|
|
|
this._loggerSink = loggerSink;
|
|
|
|
|
this._name = name;
|
|
|
|
|
this._hints = hints;
|
|
|
|
|
this._scopeName = scopeName;
|
|
|
|
|
if (record)
|
|
|
|
|
this._recording = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isEnabled(severity?: LoggerSeverity): boolean {
|
|
|
|
|
return this._loggerSink.isEnabled(this._name, severity || 'info');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verbose(message: string, ...args: any[]) {
|
|
|
|
|
return this._innerLog('verbose', message, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info(message: string, ...args: any[]) {
|
|
|
|
|
return this._innerLog('info', message, args);
|
|
|
|
|
}
|
2020-04-20 16:52:26 +02:00
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
warn(message: string, ...args: any[]) {
|
|
|
|
|
return this._innerLog('warning', message, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error(message: string | Error, ...args: any[]) {
|
|
|
|
|
return this._innerLog('error', message, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createScope(scopeName: string, record?: boolean): Logger {
|
|
|
|
|
this._loggerSink.log(this._name, 'info', `=> ${scopeName} started`, [], this._hints);
|
|
|
|
|
return new Logger(this._loggerSink, this._name, this._hints, scopeName, record);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
endScope(status: string) {
|
|
|
|
|
this._loggerSink.log(this._name, 'info', `<= ${this._scopeName} ${status}`, [], this._hints);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _innerLog(severity: LoggerSeverity, message: string | Error, ...args: any[]) {
|
|
|
|
|
if (this._recording)
|
|
|
|
|
this._recording.push(`[${this._name}] ${message}`);
|
|
|
|
|
this._loggerSink.log(this._name, severity, message, args, this._hints);
|
|
|
|
|
}
|
2020-04-20 16:52:26 +02:00
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
recording(): string[] {
|
|
|
|
|
return this._recording ? this._recording.slice() : [];
|
|
|
|
|
}
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
export class Loggers {
|
|
|
|
|
readonly api: Logger;
|
|
|
|
|
readonly browser: Logger;
|
|
|
|
|
readonly protocol: Logger;
|
|
|
|
|
|
|
|
|
|
constructor(userSink: LoggerSink | undefined) {
|
|
|
|
|
const loggerSink = new MultiplexingLoggerSink();
|
|
|
|
|
if (userSink)
|
|
|
|
|
loggerSink.add('user', userSink);
|
|
|
|
|
if (helper.isDebugMode())
|
|
|
|
|
loggerSink.add('pwdebug', new PwDebugLoggerSink());
|
|
|
|
|
loggerSink.add('debug', new DebugLoggerSink());
|
2020-04-20 16:52:26 +02:00
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
this.api = new Logger(loggerSink, 'api', { color: 'cyan' });
|
|
|
|
|
this.browser = new Logger(loggerSink, 'browser', {});
|
|
|
|
|
this.protocol = new Logger(loggerSink, 'protocol', { color: 'green' });
|
|
|
|
|
}
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
class MultiplexingLoggerSink implements LoggerSink {
|
|
|
|
|
private _loggers = new Map<string, LoggerSink>();
|
|
|
|
|
|
|
|
|
|
add(id: string, logger: LoggerSink) {
|
|
|
|
|
this._loggers.set(id, logger);
|
|
|
|
|
}
|
2020-04-20 16:52:26 +02:00
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
get(id: string): LoggerSink | undefined {
|
|
|
|
|
return this._loggers.get(id);
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
remove(id: string) {
|
|
|
|
|
this._loggers.delete(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isEnabled(name: string, severity: LoggerSeverity): boolean {
|
|
|
|
|
for (const logger of this._loggers.values()) {
|
|
|
|
|
if (logger.isEnabled(name, severity))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }) {
|
|
|
|
|
for (const logger of this._loggers.values()) {
|
|
|
|
|
if (logger.isEnabled(name, severity))
|
|
|
|
|
logger.log(name, severity, message, args, hints);
|
|
|
|
|
}
|
2020-05-20 09:10:10 +02:00
|
|
|
}
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-21 05:00:55 +02:00
|
|
|
const colorMap = new Map<string, number>([
|
|
|
|
|
['red', 160],
|
|
|
|
|
['green', 34],
|
|
|
|
|
['yellow', 172],
|
|
|
|
|
['blue', 33],
|
|
|
|
|
['magenta', 207],
|
|
|
|
|
['cyan', 45],
|
|
|
|
|
['reset', 0],
|
|
|
|
|
]);
|
2020-04-20 16:52:26 +02:00
|
|
|
|
2020-06-17 02:11:19 +02:00
|
|
|
class DebugLoggerSink {
|
2020-04-21 05:00:55 +02:00
|
|
|
private _debuggers = new Map<string, debug.IDebugger>();
|
2020-04-20 16:52:26 +02:00
|
|
|
|
|
|
|
|
isEnabled(name: string, severity: LoggerSeverity): boolean {
|
2020-04-21 08:24:53 +02:00
|
|
|
return debug.enabled(`pw:${name}`);
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }) {
|
2020-04-21 05:00:55 +02:00
|
|
|
let cachedDebugger = this._debuggers.get(name);
|
|
|
|
|
if (!cachedDebugger) {
|
2020-04-21 08:24:53 +02:00
|
|
|
cachedDebugger = debug(`pw:${name}`);
|
2020-04-21 05:00:55 +02:00
|
|
|
this._debuggers.set(name, cachedDebugger);
|
|
|
|
|
|
|
|
|
|
let color = hints.color || 'reset';
|
|
|
|
|
switch (severity) {
|
|
|
|
|
case 'error': color = 'red'; break;
|
|
|
|
|
case 'warning': color = 'yellow'; break;
|
|
|
|
|
}
|
|
|
|
|
const escaped = colorMap.get(color) || 0;
|
|
|
|
|
if (escaped)
|
|
|
|
|
(cachedDebugger as any).color = String(escaped);
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
2020-04-21 05:00:55 +02:00
|
|
|
cachedDebugger(message, ...args);
|
2020-04-20 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-17 02:11:19 +02:00
|
|
|
|
|
|
|
|
class PwDebugLoggerSink {
|
|
|
|
|
isEnabled(name: string, severity: LoggerSeverity): boolean {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }) {
|
|
|
|
|
}
|
|
|
|
|
}
|