import { createLogger as createBunyanLogger, LoggerOptions as BunyanLoggerOptions, Stream as BunyanLogStreamOptions, } from 'bunyan'; // tslint:disable-next-line: no-duplicate-imports import * as BunyanLogger from 'bunyan'; import { cloneDeep, get, has, isObject } from 'lodash'; import * as path from 'path'; // import * as bunyanDebugStream from 'bunyan-debug-stream'; import { ConsoleStreamOptions, createConsoleStream } from './ConsoleStream'; import { mkdirpSync } from './lib'; import { Logger, LoggerConfig, LogLevelString } from './types'; import { Writable } from 'stream'; export * from './ConsoleStream/types'; export const parseLogLevel = (level: string, defaultLevel?: LogLevelString): LogLevelString => { const validLevels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal']; const logLevel = level ? validLevels.indexOf(level.toLowerCase()) >= 0 ? level.toLowerCase() : defaultLevel : defaultLevel; return logLevel as LogLevelString; }; export const createConsoleLogger = ( name: string, level: LogLevelString = 'info', src = true, options: ConsoleStreamOptions = {} ): Logger => { BunyanLogger.prototype['write'] = (out: string) => { process.stdout.write(out); }; BunyanLogger.prototype['stdout'] = process.stdout; const serializers = { req: BunyanLogger.stdSerializers.req, res: BunyanLogger.stdSerializers.res, err: BunyanLogger.stdSerializers.err, }; const log = createBunyanLogger({ name, src, level, streams: [ { level, type: 'raw', stream: createConsoleStream(options), }, ], serializers, }); return (log as unknown) as Logger; }; /** * Creates a logger that doesn't write output anywhere * @param name * @param level */ export const createNoopLogger = ( name: string, level: LogLevelString = 'info', ): Logger => { // not sure if this is the best way to do this.. const log = createBunyanLogger({ name, level, streams: [ { level, type: 'raw', stream: new Writable({ objectMode: true, write(chunk, encoding, callback) { setImmediate(callback); } }) }, ], }); return (log as unknown) as Logger; }; const createConsoleStreamOptions = (level: LogLevelString, pretty = true) => { const stream = pretty ? createConsoleStream({ basepath: process.cwd(), // this should be the root folder of your project. forceColor: false, }) : process.stdout; return { level, type: pretty ? 'raw' : 'stream', stream, }; }; export const createLogger = (loggerConfig: LoggerConfig): Logger => { const defaultOptions = cloneDeep(loggerConfig.defaultOptions); //console.dir(defaultOptions); const pretty = get(defaultOptions, 'stdOut.pretty', true); //console.log(`pretty: ${pretty}`); // const serializers = pretty // ? bunyanDebugStream.serializers // : { // req: BunyanLogger.stdSerializers.req, // res: BunyanLogger.stdSerializers.res, // err: BunyanLogger.stdSerializers.err // }; const serializers = { req: BunyanLogger.stdSerializers.req, res: BunyanLogger.stdSerializers.res, err: BunyanLogger.stdSerializers.err, }; const bunyanOptions: BunyanLoggerOptions = { ...defaultOptions, serializers, // serializers: { // req: BunyanLogger.stdSerializers.req, // res: BunyanLogger.stdSerializers.res, // err: BunyanLogger.stdSerializers.err // } }; const defaultLevel = get(defaultOptions, 'level', 'info'); // can't define a real stream in config file, so have to create it here using custom config def if (defaultOptions.stdOut) { if (!bunyanOptions.streams) { bunyanOptions.streams = []; } let stdOutStreamOptions: BunyanLogStreamOptions; if (isObject(defaultOptions.stdOut)) { stdOutStreamOptions = { //stream: process.stdout, ...createConsoleStreamOptions(get(loggerConfig.defaultOptions.stdOut, 'level', defaultLevel), pretty), //...(defaultOptions.stdOut as {}), }; } else { stdOutStreamOptions = { ...createConsoleStreamOptions(defaultLevel as LogLevelString, pretty), }; } if (stdOutStreamOptions) { bunyanOptions.streams.push(stdOutStreamOptions); } delete bunyanOptions.stdOut; } createLogDirs(bunyanOptions); BunyanLogger.prototype['write'] = (out: string) => { process.stdout.write(out); }; BunyanLogger.prototype['stdout'] = process.stdout; const logger = createBunyanLogger(bunyanOptions); return (logger as unknown) as Logger; }; const createLogDirs = (bunyanOptions: BunyanLoggerOptions) => { bunyanOptions.streams.map(s => { if (s.path) { mkdirpSync(path.dirname(s.path)); } }); };