Last active
June 4, 2018 19:53
-
-
Save nknapp/3458e7ec0704061834b113f0828900e9 to your computer and use it in GitHub Desktop.
A logger that I didn't want to throw away, but I'm currently not needing it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { expect } from "chai"; | |
import { Writable } from "stream"; | |
import { logLevel, LogManager, toStream } from "../../src/utils/logger"; | |
describe("The logger", () => { | |
describe("The 'for'-function", function() { | |
const logEntries: any[] = []; | |
const log = new LogManager(); | |
let stream: Writable; | |
let dateNow: any; | |
let lastDate: number = 5000 | |
beforeEach(function() { | |
dateNow = Date.now; | |
Date.now = () => lastDate += 15; | |
stream = new Writable({ | |
write(chunk: Buffer | string, encoding: string, cb: Function): boolean { | |
logEntries.push(chunk.toString()); | |
cb(null); | |
return true; | |
} | |
}); | |
logEntries.length = 0; | |
log.configure([], toStream(stream), logLevel.debug); | |
}); | |
afterEach(function() { | |
Date.now = dateNow | |
}); | |
it("return a preconfigured LogFacade object", function() { | |
const logger = log.for("testchannel"); | |
logger.debug(1, 2, 3); | |
logger.info(2, 3, 4); | |
logger.warn(3, 4, 5); | |
logger.error(4, 5, 6); | |
expect(logEntries).to.deep.equal([ | |
"1970-01-01T00:00:05.015Z debug testchannel 1 2 3", | |
"+15 info testchannel 2 3 4", | |
"+15 warn testchannel 3 4 5", | |
"+15 error testchannel 4 5 6" | |
]); | |
}); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Stream, Writable } from 'stream'; | |
import * as util from 'util'; | |
export const logLevel = { | |
debug: 1, | |
info: 10, | |
warn: 20, | |
error: 30, | |
}; | |
// Level number -> level name | |
const levelName: { [levelNr: number]: string } = {}; | |
Object.keys(logLevel).forEach(levelStr => { | |
levelName[logLevel[levelStr]] = levelStr; | |
}); | |
export interface IChannelConfig { | |
/** | |
* The log-level of this config | |
*/ | |
logLevel: number; | |
channelMatch: RegExp | string; | |
/** | |
* The log function that performs the actual logging (such as "console.log") | |
* @param channel | |
* @param logLevel | |
* @param args | |
*/ | |
logFn(channel: string, logLevel: number, ...args: any[]): void; | |
} | |
export interface ILogFacade { | |
debug(...args: any[]): void; | |
info(...args: any[]): void; | |
warn(...args: any[]): void; | |
error(...args: any[]): void; | |
} | |
// Timestamp of the last log output | |
let lastLog: number | null = null; | |
export function toStream(stream: Writable) { | |
return function out(channel: string, level: number, ...args) { | |
const str = util.format.apply(util, args); | |
const now = Date.now(); | |
let dateStr: string; | |
dateStr = lastLog ? `+${now - lastLog}` : new Date(now).toISOString(); | |
lastLog = now; | |
stream.write(`${dateStr} ${levelName[level]} ${channel} ${str}`); | |
}; | |
} | |
export class LogManager { | |
private channelConfigs: IChannelConfig[] = []; | |
private defaultConfig: IChannelConfig = { | |
channelMatch: /.*/, | |
logLevel: logLevel.info, | |
logFn: toStream(process.stdout), | |
}; | |
public configure(channelConfigs: IChannelConfig[], defaultLogger: (...args) => void, defaultLogLevel: number): void { | |
this.channelConfigs = channelConfigs; | |
this.defaultConfig.logFn = defaultLogger; | |
this.defaultConfig.logLevel = defaultLogLevel; | |
} | |
public for(channel: string): ILogFacade { | |
return { | |
debug: (...args: any[]) => this.log(channel, logLevel.debug, ...args), | |
info: (...args: any[]) => this.log(channel, logLevel.info, ...args), | |
warn: (...args: any[]) => this.log(channel, logLevel.warn, ...args), | |
error: (...args: any[]) => this.log(channel, logLevel.error, ...args), | |
}; | |
} | |
private log(channel: string, level: number, ...args: any[]): void { | |
const matchingConfig = | |
this.channelConfigs.find(config => { | |
if (config.channelMatch instanceof RegExp) { | |
return config.channelMatch.test(channel); | |
} else { | |
// string | |
return config.channelMatch === channel; | |
} | |
}) || this.defaultConfig; | |
if (matchingConfig.logLevel <= level) { | |
matchingConfig.logFn(channel, level, ...args); | |
} | |
} | |
} | |
export const log = new LogManager(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I wanted something more like log4j, with channels and levels and a centralized configuration. It's not finished, but I am sticking to
pino
fo now.