import * as winston from 'winston'; import {LeveledLogMethod} from 'winston'; import {RequestHandler} from 'express'; import * as colors from 'colors'; import {DefaultConfig} from '.'; import prune = require('json-prune'); const logger = winston.createLogger({ level: DefaultConfig.isProduction ? 'info' : 'silly', format: winston.format.json(), transports: [ new winston.transports.Console({ format: winston.format.simple(), }), ], }); export const levels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'] as const; export type LogLevels = typeof levels[number]|'log'; const wrapper = (original: LeveledLogMethod) => { return (...args: unknown[]) => { return original(args.map((obj) => typeof obj === 'string' ? obj : prune(obj)).join(' ')); }; }; export const Logger = { } as {[level in LogLevels]: ReturnType}; for (const level of levels) { Logger[level] = wrapper(logger[level]); } Logger.log = wrapper(logger['silly']); export const HttpLogger: RequestHandler = (req, res, next) => { const start = Date.now(); const path = req.path; type Callback = (() => void)|undefined; const end = res.end; res.end = function(...args: [Callback] & [unknown, Callback] & [unknown, BufferEncoding, Callback]) { const statusCode = res.statusCode; const colorFunction = statusCode >= 500 ? colors.red : statusCode >= 400 ? colors.yellow : statusCode >= 300 ? colors.cyan : statusCode >= 200 ? colors.green : colors.gray; const status = colorFunction(res.statusCode.toString(10)); const method = req.method.toUpperCase().padEnd(6, ' '); const responseTime = (Date.now()-start).toString(10).padStart(3, ' '); if (!req.noHttpLogging) Logger.http(`${status} ${method} ${responseTime}ms ${path}`); end.apply(res, args); }; next(); };