139 lines
3.5 KiB
TypeScript
139 lines
3.5 KiB
TypeScript
import {ILogger} from './ILogger'
|
|
import {ILoggerFactory} from './ILoggerFactory'
|
|
import {createLogger, format, transports} from 'winston'
|
|
|
|
// logging can be configured via environment variables, for example:
|
|
// `LOG='*:info,api:debug,-sql' node ...` sets all logs to info, api to debug,
|
|
// and disable all sql logs.
|
|
|
|
export function pad(text: string, n: number, trim: boolean) {
|
|
text = String(text)
|
|
if (text.length >= n) {
|
|
return trim ? text.substring(0, n) : text
|
|
}
|
|
while (text.length < n) {
|
|
text += ' '
|
|
}
|
|
return text
|
|
}
|
|
|
|
export type ILogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose' | 'off'
|
|
|
|
export interface IEnabledLoggers {
|
|
readonly [key: string]: ILogLevel
|
|
}
|
|
|
|
export interface IParams {
|
|
readonly json: boolean
|
|
readonly color: boolean
|
|
readonly logstash: boolean,
|
|
}
|
|
|
|
export interface ILoggerOptions {
|
|
readonly enabledLoggers: IEnabledLoggers
|
|
readonly levelPad: number
|
|
readonly namePad: number
|
|
readonly params: IParams
|
|
}
|
|
|
|
export class LoggerFactory implements ILoggerFactory {
|
|
|
|
protected readonly defaultLogLevel: string
|
|
protected readonly loggers: {[key: string]: ILogger} = {}
|
|
getCorrelationId: () => string
|
|
|
|
static createFromEnv({
|
|
logs = process.env.LOG || '',
|
|
opts = process.env.LOG_OPTS || '',
|
|
} = {}) {
|
|
const enabledLoggers = logs.split(',').reduce((logConfig, log) => {
|
|
const [key, value] = log.split(':')
|
|
logConfig[key] = (value || 'info') as ILogLevel
|
|
return logConfig
|
|
}, {} as {[key: string]: ILogLevel})
|
|
|
|
const params = opts.split(',').reduce((o, key) => {
|
|
o[key] = true
|
|
return o
|
|
}, {} as {[key: string]: boolean})
|
|
|
|
return new this({
|
|
enabledLoggers,
|
|
levelPad: 5,
|
|
namePad: 5,
|
|
params: params as any as IParams,
|
|
})
|
|
}
|
|
|
|
constructor(readonly options: ILoggerOptions) {
|
|
this.defaultLogLevel = options.enabledLoggers['*'] || 'off'
|
|
this.getCorrelationId = () => ''
|
|
}
|
|
|
|
getLoggerLevel(name: string): ILogLevel {
|
|
const {enabledLoggers} = this.options
|
|
const disabled = !!enabledLoggers['-' + name]
|
|
if (disabled) {
|
|
return 'off'
|
|
}
|
|
return enabledLoggers[name] || this.defaultLogLevel
|
|
}
|
|
|
|
getLogger = (name: string): ILogger => {
|
|
if (this.loggers[name]) {
|
|
return this.loggers[name]
|
|
}
|
|
const {levelPad, namePad, params} = this.options
|
|
|
|
const addName = format((info, opts) => {
|
|
info.name = name
|
|
return info
|
|
})
|
|
|
|
const prettyFormat = format(info => {
|
|
info.message = info.timestamp + ' ' +
|
|
pad(info.level, levelPad, true) + ' ' +
|
|
pad(name, namePad, true) + ' ' +
|
|
this.getCorrelationId() + ' ' +
|
|
info.message
|
|
return info
|
|
})
|
|
|
|
const print = format.printf(info => {
|
|
return info.message
|
|
})
|
|
|
|
const formatters = [
|
|
addName(),
|
|
format.timestamp(),
|
|
format.splat(),
|
|
].filter(f => !!f)
|
|
|
|
if (params.logstash) {
|
|
formatters.push(format.logstash())
|
|
} else if (params.json) {
|
|
formatters.push(format.json())
|
|
} else if (params.color) {
|
|
formatters.push(prettyFormat())
|
|
formatters.push(format.colorize({ all: true }))
|
|
formatters.push(print)
|
|
} else {
|
|
formatters.push(prettyFormat())
|
|
formatters.push(print)
|
|
}
|
|
|
|
const logger = this.loggers[name] = createLogger({
|
|
format: format.combine.apply(format, formatters),
|
|
transports: [
|
|
new transports.Console({
|
|
handleExceptions: false,
|
|
level: this.getLoggerLevel(name),
|
|
}),
|
|
],
|
|
})
|
|
return logger
|
|
}
|
|
}
|
|
|
|
export const loggerFactory = LoggerFactory.createFromEnv()
|