refactor: separate logger for web env
This commit is contained in:
28
src/logging/ConsoleLoggerFactory.ts
Normal file
28
src/logging/ConsoleLoggerFactory.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Logger, LogLevel } from '@smartweave';
|
||||||
|
import { LoggerOptions } from 'winston';
|
||||||
|
import { ConsoleLogger } from './web/ConsoleLogger';
|
||||||
|
|
||||||
|
export class ConsoleLoggerFactory {
|
||||||
|
constructor() {
|
||||||
|
this.setOptions = this.setOptions.bind(this);
|
||||||
|
this.getOptions = this.getOptions.bind(this);
|
||||||
|
this.create = this.create.bind(this);
|
||||||
|
this.logLevel = this.logLevel.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOptions(newOptions: LoggerOptions, moduleName?: string): void {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions(moduleName?: string): LoggerOptions {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
logLevel(level: LogLevel, moduleName?: string) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
create(moduleName = 'SWC'): Logger {
|
||||||
|
return new ConsoleLogger();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/logging/Logger.ts
Normal file
19
src/logging/Logger.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export type LogLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly';
|
||||||
|
|
||||||
|
export interface Logger {
|
||||||
|
profile(id: any);
|
||||||
|
|
||||||
|
error(message?: any, ...optionalParams: any[]);
|
||||||
|
|
||||||
|
warn(message?: any, ...optionalParams: any[]);
|
||||||
|
|
||||||
|
info(message?: any, ...optionalParams: any[]);
|
||||||
|
|
||||||
|
verbose(message?: any, ...optionalParams: any[]);
|
||||||
|
|
||||||
|
debug(message?: any, ...optionalParams: any[]);
|
||||||
|
|
||||||
|
silly(message?: any, ...optionalParams: any[]);
|
||||||
|
|
||||||
|
log(message?: any, ...optionalParams: any[]);
|
||||||
|
}
|
||||||
@@ -1,125 +1,25 @@
|
|||||||
import winston, { createLogger, format, LogEntry, Logger, LoggerOptions, transports } from 'winston';
|
import { Logger, LogLevel } from '@smartweave';
|
||||||
import path from 'path';
|
import { LoggerOptions } from 'winston';
|
||||||
|
import { ConsoleLoggerFactory } from './ConsoleLoggerFactory';
|
||||||
|
import { WinstonLoggerFactory } from './node/WinstonLoggerFactory';
|
||||||
|
|
||||||
const { combine, errors, timestamp, colorize, printf } = format;
|
|
||||||
|
|
||||||
export const baseFormat = combine(
|
|
||||||
timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
|
|
||||||
errors({ stack: true }),
|
|
||||||
format.splat(),
|
|
||||||
format((info) => {
|
|
||||||
info.level = info.level.toUpperCase();
|
|
||||||
return info;
|
|
||||||
})()
|
|
||||||
);
|
|
||||||
|
|
||||||
winston.addColors({
|
|
||||||
error: 'bold redBG',
|
|
||||||
warn: 'bold magenta',
|
|
||||||
info: 'bold green',
|
|
||||||
http: 'bold magentaBG',
|
|
||||||
verbose: 'bold cyan',
|
|
||||||
debug: 'bold blue',
|
|
||||||
silly: 'grey'
|
|
||||||
});
|
|
||||||
|
|
||||||
export const prettyFormat = combine(
|
|
||||||
baseFormat,
|
|
||||||
colorize({ all: false }),
|
|
||||||
printf(({ timestamp, level, message, ...rest }) => {
|
|
||||||
let result = `[${timestamp}] [${rest.module || 'SWC'}] ${level}: ${message}`;
|
|
||||||
if (rest?.durationMs) {
|
|
||||||
result += ` - ${rest.durationMs}ms`;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
export const defaultLoggerOptions = {
|
|
||||||
level: 'debug',
|
|
||||||
format: prettyFormat,
|
|
||||||
transports: [new transports.Console()],
|
|
||||||
exitOnError: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LogLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around "Winston" logging library that allows to change logging settings at runtime
|
|
||||||
* (for each registered module independently, or globally - for all loggers).
|
|
||||||
*/
|
|
||||||
export class LoggerFactory {
|
export class LoggerFactory {
|
||||||
static readonly INST: LoggerFactory = new LoggerFactory();
|
static readonly INST: LoggerFactory =
|
||||||
|
typeof window === 'undefined' ? new WinstonLoggerFactory() : new ConsoleLoggerFactory();
|
||||||
|
|
||||||
private readonly registeredLoggers: { [moduleName: string]: Logger } = {};
|
setOptions(newOptions: LoggerOptions, moduleName: string): void {
|
||||||
private readonly registeredOptions: { [moduleName: string]: LoggerOptions } = {};
|
LoggerFactory.INST.setOptions(newOptions, moduleName);
|
||||||
|
|
||||||
private defaultOptions: LoggerOptions = { ...defaultLoggerOptions };
|
|
||||||
|
|
||||||
private constructor() {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
setOptions(newOptions: LoggerOptions, moduleName?: string): void {
|
|
||||||
// if moduleName not specified
|
|
||||||
if (!moduleName) {
|
|
||||||
// update default options
|
|
||||||
LoggerFactory.INST.defaultOptions = newOptions;
|
|
||||||
// update options for all already registered loggers
|
|
||||||
Object.keys(this.registeredLoggers).forEach((key: string) => {
|
|
||||||
Object.assign(this.registeredLoggers[key], newOptions);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// if logger already registered
|
|
||||||
if (this.registeredLoggers[moduleName]) {
|
|
||||||
// update its options
|
|
||||||
Object.assign(this.registeredLoggers[moduleName], newOptions);
|
|
||||||
} else {
|
|
||||||
// if logger not yet registered - save options that will be used for its creation
|
|
||||||
this.registeredOptions[moduleName] = {
|
|
||||||
...this.defaultOptions,
|
|
||||||
...newOptions
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions(moduleName?: string): LoggerOptions {
|
getOptions(moduleName?: string): LoggerOptions {
|
||||||
if (!moduleName) {
|
return LoggerFactory.INST.getOptions(moduleName);
|
||||||
return this.defaultOptions;
|
|
||||||
} else {
|
|
||||||
if (this.registeredLoggers[moduleName]) {
|
|
||||||
return this.registeredLoggers[moduleName];
|
|
||||||
} else if (this.registeredOptions[moduleName]) {
|
|
||||||
return this.registeredOptions[moduleName];
|
|
||||||
} else {
|
|
||||||
return this.defaultOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logLevel(level: LogLevel, moduleName?: string) {
|
logLevel(level: LogLevel, moduleName?: string) {
|
||||||
this.setOptions({ level }, moduleName);
|
LoggerFactory.INST.logLevel(level, moduleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(moduleName = 'SWC'): Logger {
|
create(moduleName?: string): Logger {
|
||||||
// in case of passing '__dirname' as moduleName - leaves only the file name without extension.
|
return LoggerFactory.INST.create(moduleName);
|
||||||
const normalizedModuleName = path.basename(moduleName, path.extname(moduleName));
|
|
||||||
if (!this.registeredLoggers[normalizedModuleName]) {
|
|
||||||
const logger = createLogger({
|
|
||||||
...this.getOptions(normalizedModuleName),
|
|
||||||
// note: profiler this not currently honor defaultMeta - https://github.com/winstonjs/winston/pull/1935
|
|
||||||
defaultMeta: { module: normalizedModuleName }
|
|
||||||
});
|
|
||||||
// note: winston by default logs profile message with info level (to high IMO),
|
|
||||||
// with no option to set different default - so we're forcing level by
|
|
||||||
// overwriting default function...
|
|
||||||
const originalProfile = logger.profile.bind(logger);
|
|
||||||
logger.profile = (id: string | number, meta?: LogEntry) => {
|
|
||||||
return originalProfile(id, meta || { message: '', level: 'debug' });
|
|
||||||
};
|
|
||||||
this.registeredLoggers[normalizedModuleName] = logger;
|
|
||||||
}
|
|
||||||
return this.registeredLoggers[normalizedModuleName];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
|
export * from './web/ConsoleLogger';
|
||||||
|
export * from './ConsoleLoggerFactory';
|
||||||
|
export * from './node/WinstonLoggerFactory';
|
||||||
|
export * from './Logger';
|
||||||
export * from './LoggerFactory';
|
export * from './LoggerFactory';
|
||||||
|
|||||||
124
src/logging/node/WinstonLoggerFactory.ts
Normal file
124
src/logging/node/WinstonLoggerFactory.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import winston, { createLogger, format, LogEntry, LoggerOptions, transports } from 'winston';
|
||||||
|
import path from 'path';
|
||||||
|
import { Logger, LogLevel } from '@smartweave';
|
||||||
|
|
||||||
|
const { combine, errors, timestamp, colorize, printf } = format;
|
||||||
|
/**
|
||||||
|
* A wrapper around "Winston" logging library that allows to change logging settings at runtime
|
||||||
|
* (for each registered module independently, or globally - for all loggers).
|
||||||
|
*/
|
||||||
|
export class WinstonLoggerFactory {
|
||||||
|
public readonly baseFormat = combine(
|
||||||
|
timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
|
||||||
|
errors({ stack: true }),
|
||||||
|
format.splat(),
|
||||||
|
format((info) => {
|
||||||
|
info.level = info.level.toUpperCase();
|
||||||
|
return info;
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
|
public readonly prettyFormat = combine(
|
||||||
|
this.baseFormat,
|
||||||
|
colorize({ all: false }),
|
||||||
|
printf(({ timestamp, level, message, ...rest }) => {
|
||||||
|
let result = `[${timestamp}] [${rest.module || 'SWC'}] ${level}: ${message}`;
|
||||||
|
if (rest?.durationMs) {
|
||||||
|
result += ` - ${rest.durationMs}ms`;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
private readonly registeredLoggers: { [moduleName: string]: Logger } = {};
|
||||||
|
private readonly registeredOptions: { [moduleName: string]: LoggerOptions } = {};
|
||||||
|
|
||||||
|
private defaultOptions: LoggerOptions = {
|
||||||
|
level: 'debug',
|
||||||
|
format: this.prettyFormat,
|
||||||
|
transports: [new transports.Console()],
|
||||||
|
exitOnError: false
|
||||||
|
};
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
winston.addColors({
|
||||||
|
error: 'bold redBG',
|
||||||
|
warn: 'bold magenta',
|
||||||
|
info: 'bold green',
|
||||||
|
http: 'bold magentaBG',
|
||||||
|
verbose: 'bold cyan',
|
||||||
|
debug: 'bold blue',
|
||||||
|
silly: 'grey'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setOptions = this.setOptions.bind(this);
|
||||||
|
this.getOptions = this.getOptions.bind(this);
|
||||||
|
this.create = this.create.bind(this);
|
||||||
|
this.logLevel = this.logLevel.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOptions(newOptions: LoggerOptions, moduleName?: string): void {
|
||||||
|
// if moduleName not specified
|
||||||
|
if (!moduleName) {
|
||||||
|
// update default options
|
||||||
|
this.defaultOptions = newOptions;
|
||||||
|
// update options for all already registered loggers
|
||||||
|
Object.keys(this.registeredLoggers).forEach((key: string) => {
|
||||||
|
Object.assign(this.registeredLoggers[key], newOptions);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// if logger already registered
|
||||||
|
if (this.registeredLoggers[moduleName]) {
|
||||||
|
// update its options
|
||||||
|
Object.assign(this.registeredLoggers[moduleName], newOptions);
|
||||||
|
} else {
|
||||||
|
// if logger not yet registered - save options that will be used for its creation
|
||||||
|
this.registeredOptions[moduleName] = {
|
||||||
|
...this.defaultOptions,
|
||||||
|
...newOptions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions(moduleName?: string): LoggerOptions {
|
||||||
|
if (!moduleName) {
|
||||||
|
return this.defaultOptions;
|
||||||
|
} else {
|
||||||
|
if (this.registeredLoggers[moduleName]) {
|
||||||
|
// safe typecast in this case...
|
||||||
|
return this.registeredLoggers[moduleName] as LoggerOptions;
|
||||||
|
} else if (this.registeredOptions[moduleName]) {
|
||||||
|
return this.registeredOptions[moduleName];
|
||||||
|
} else {
|
||||||
|
return this.defaultOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logLevel(level: LogLevel, moduleName?: string) {
|
||||||
|
this.setOptions({ level }, moduleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(moduleName = 'SWC'): Logger {
|
||||||
|
// in case of passing '__dirname' as moduleName - leaves only the file name without extension.
|
||||||
|
const normalizedModuleName = path.basename(moduleName, path.extname(moduleName));
|
||||||
|
if (!this.registeredLoggers[normalizedModuleName]) {
|
||||||
|
const logger = createLogger({
|
||||||
|
...this.getOptions(normalizedModuleName),
|
||||||
|
// note: profiler this not currently honor defaultMeta - https://github.com/winstonjs/winston/pull/1935
|
||||||
|
defaultMeta: { module: normalizedModuleName }
|
||||||
|
});
|
||||||
|
// note: winston by default logs profile message with info level (to high IMO),
|
||||||
|
// with no option to set different default - so we're forcing level by
|
||||||
|
// overwriting default function...
|
||||||
|
const originalProfile = logger.profile.bind(logger);
|
||||||
|
logger.profile = (id: string | number, meta?: LogEntry) => {
|
||||||
|
return originalProfile(id, meta || { message: '', level: 'debug' });
|
||||||
|
};
|
||||||
|
this.registeredLoggers[normalizedModuleName] = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.registeredLoggers[normalizedModuleName];
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/logging/web/ConsoleLogger.ts
Normal file
35
src/logging/web/ConsoleLogger.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Logger } from '@smartweave';
|
||||||
|
|
||||||
|
export class ConsoleLogger implements Logger {
|
||||||
|
debug(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.debug(message, optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.error(message, optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.info(message, optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile(id: any) {
|
||||||
|
console.warn('Profile not implemented for this logger!');
|
||||||
|
}
|
||||||
|
|
||||||
|
silly(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.debug();
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.debug(message, optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.warn(message, optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(message?: any, ...optionalParams: any[]) {
|
||||||
|
console.info(message, optionalParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user