Refactor logger
This commit is contained in:
parent
c444a03884
commit
d2b58fac6f
@ -1,26 +1,78 @@
|
||||
import util from 'util';
|
||||
|
||||
type LogType = 'info' | 'debug' | 'warn' | 'error';
|
||||
import { format } from 'util';
|
||||
|
||||
/**
|
||||
* Logger for all of Squeebot. Use this instead of console.log/warn/error!
|
||||
* Log level
|
||||
*/
|
||||
export enum LogLevel {
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
DEBUG,
|
||||
}
|
||||
|
||||
export type LogListener = (
|
||||
ltype: LogLevel,
|
||||
...data: any[]
|
||||
) => void | Promise<void>;
|
||||
|
||||
export type ConsoleFunction = (...data: any[]) => void;
|
||||
export type ConsoleLogFunction = ConsoleFunction;
|
||||
export type ConsoleWarnFunction = ConsoleFunction;
|
||||
export type ConsoleErrorFunction = ConsoleFunction;
|
||||
|
||||
/**
|
||||
* Logger for all of Squeebot. Use this instead of console.log/warn/error in your plugins!
|
||||
*/
|
||||
export class Logger {
|
||||
private console = [console.log, console.warn, console.error];
|
||||
/**
|
||||
* The `console.*` functions used for logging convenience.
|
||||
*
|
||||
* Defaults to `console.log`, `console.warn`, `console.error`.
|
||||
*
|
||||
* Can really be anything that accepts the input for `node:util.format`.
|
||||
*/
|
||||
public console: [
|
||||
ConsoleLogFunction,
|
||||
ConsoleWarnFunction,
|
||||
ConsoleErrorFunction
|
||||
] = [console.log, console.warn, console.error];
|
||||
|
||||
constructor(
|
||||
public timestamp = 'dd/mm/yy HH:MM:ss'
|
||||
) {}
|
||||
/**
|
||||
* External Logger log event listeners.
|
||||
*/
|
||||
protected listeners: LogListener[] = [];
|
||||
|
||||
public dateFmt(date: Date) {
|
||||
return date.toISOString()
|
||||
.replace(/T/, ' ')
|
||||
.replace(/\..+/, '');
|
||||
constructor(public timestamp = 'dd/mm/yy HH:MM:ss') {}
|
||||
|
||||
public formatDate(date: Date): string {
|
||||
return date.toISOString().replace(/T/, ' ').replace(/\..+/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set node.js readline consideration
|
||||
* Add a logger listener, useful for redirecting logger output somewhere else.
|
||||
* Will not await any Promises but it will catch unhandled rejections - please do not depend on this.
|
||||
* @param fn Log listener
|
||||
*/
|
||||
public listen(fn: LogListener): void {
|
||||
this.listeners.push(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a logger listener.
|
||||
* @param fn Log listener
|
||||
* @returns nothing
|
||||
*/
|
||||
public unlisten(fn: LogListener): void {
|
||||
const inx = this.listeners.indexOf(fn);
|
||||
if (inx === -1) return;
|
||||
this.listeners.splice(inx, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set node.js readline consideration.
|
||||
* @param rl Readline instance
|
||||
* @see Logger.console - the array modified by this function
|
||||
* @see Logger.resetConsole - the "undo" to this function
|
||||
*/
|
||||
public setReadline(rl: any): void {
|
||||
for (const index in this.console) {
|
||||
@ -34,32 +86,41 @@ export class Logger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out to log
|
||||
* @param ltype Logger level
|
||||
* @param data Data to log
|
||||
* Reset output consoles to default. Useful for dynamically detaching readline.
|
||||
* @see Logger.console
|
||||
*/
|
||||
private write(ltype: LogType, ...data: any[]): void {
|
||||
public resetConsole() {
|
||||
this.console = [console.log, console.warn, console.error];
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out to log
|
||||
* @param logLevel Logger level
|
||||
* @param data Data to log. Will be sent to `node:util.format`
|
||||
* @see util.format
|
||||
*/
|
||||
public write(logLevel: LogLevel, ...data: any[]): void {
|
||||
const message = [];
|
||||
let cfunc = this.console[0];
|
||||
let outputFunction = this.console[0];
|
||||
|
||||
if (this.timestamp) {
|
||||
message.push(`[${this.dateFmt(new Date())}]`);
|
||||
message.push(`[${this.formatDate(new Date())}]`);
|
||||
}
|
||||
|
||||
switch (ltype) {
|
||||
case 'info':
|
||||
switch (logLevel) {
|
||||
case LogLevel.INFO:
|
||||
message.push('[ INFO]');
|
||||
break;
|
||||
case 'debug':
|
||||
case LogLevel.DEBUG:
|
||||
message.push('[DEBUG]');
|
||||
break;
|
||||
case 'warn':
|
||||
case LogLevel.WARN:
|
||||
message.push('[ WARN]');
|
||||
cfunc = this.console[1];
|
||||
outputFunction = this.console[1];
|
||||
break;
|
||||
case 'error':
|
||||
case LogLevel.ERROR:
|
||||
message.push('[ERROR]');
|
||||
cfunc = this.console[2];
|
||||
outputFunction = this.console[2];
|
||||
break;
|
||||
}
|
||||
|
||||
@ -67,10 +128,13 @@ export class Logger {
|
||||
let final = data[0];
|
||||
if (data.length > 1) {
|
||||
const fargs = data.slice(1);
|
||||
final = util.format(data[0], ...fargs);
|
||||
final = format(data[0], ...fargs);
|
||||
}
|
||||
message.push(final);
|
||||
cfunc(...message);
|
||||
|
||||
// Notify listeners and output
|
||||
this.notify(logLevel, ...message);
|
||||
outputFunction(...message);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +143,7 @@ export class Logger {
|
||||
* See `console.log` for more information.
|
||||
*/
|
||||
public log(...data: any[]): void {
|
||||
this.write('info', ...data);
|
||||
this.write(LogLevel.INFO, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,7 +152,7 @@ export class Logger {
|
||||
* See `console.warn` for more information.
|
||||
*/
|
||||
public warn(...data: any[]): void {
|
||||
this.write('warn', ...data);
|
||||
this.write(LogLevel.WARN, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +161,7 @@ export class Logger {
|
||||
* See `console.log` for more information.
|
||||
*/
|
||||
public info(...data: any[]): void {
|
||||
this.write('info', ...data);
|
||||
this.write(LogLevel.INFO, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +170,7 @@ export class Logger {
|
||||
* See `console.error` for more information.
|
||||
*/
|
||||
public error(...data: any[]): void {
|
||||
this.write('error', ...data);
|
||||
this.write(LogLevel.ERROR, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +179,34 @@ export class Logger {
|
||||
* See `console.log` for more information.
|
||||
*/
|
||||
public debug(...data: any[]): void {
|
||||
this.write('debug', ...data);
|
||||
this.write(LogLevel.DEBUG, ...data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify logger listeners about new lines. Catches uncaught errors.
|
||||
* @param level Log level
|
||||
* @param data Log data
|
||||
*/
|
||||
protected notify(level: LogLevel, ...data: any): void {
|
||||
for (const listener of this.listeners) {
|
||||
try {
|
||||
const resp = listener.call(null, level, ...data);
|
||||
// Catch Promise errors
|
||||
if (resp instanceof Promise) {
|
||||
resp.catch((err) =>
|
||||
process.stderr.write(
|
||||
`A Logger listener threw an unhandled rejection: ${err.stack}\r\n`
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
process.stderr.write(
|
||||
`A Logger listener threw an unhandled error: ${
|
||||
(err as Error).stack
|
||||
}\r\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user