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 path from 'path';
|
||||
import { Logger, LogLevel } from '@smartweave';
|
||||
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 {
|
||||
static readonly INST: LoggerFactory = new LoggerFactory();
|
||||
static readonly INST: LoggerFactory =
|
||||
typeof window === 'undefined' ? new WinstonLoggerFactory() : new ConsoleLoggerFactory();
|
||||
|
||||
private readonly registeredLoggers: { [moduleName: string]: Logger } = {};
|
||||
private readonly registeredOptions: { [moduleName: string]: LoggerOptions } = {};
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
setOptions(newOptions: LoggerOptions, moduleName: string): void {
|
||||
LoggerFactory.INST.setOptions(newOptions, moduleName);
|
||||
}
|
||||
|
||||
getOptions(moduleName?: string): LoggerOptions {
|
||||
if (!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;
|
||||
}
|
||||
}
|
||||
return LoggerFactory.INST.getOptions(moduleName);
|
||||
}
|
||||
|
||||
logLevel(level: LogLevel, moduleName?: string) {
|
||||
this.setOptions({ level }, moduleName);
|
||||
LoggerFactory.INST.logLevel(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];
|
||||
create(moduleName?: string): Logger {
|
||||
return LoggerFactory.INST.create(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
export * from './web/ConsoleLogger';
|
||||
export * from './ConsoleLoggerFactory';
|
||||
export * from './node/WinstonLoggerFactory';
|
||||
export * from './Logger';
|
||||
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