Estoy usando winston para iniciar sesión y la mayoría de las veces funciona bien, pero cuando hay una excepción, simplemente no imprime ningún detalle.
Aquí está mi código para configurar winston :
// Create logger const logger = winston.createLogger() // Create timestamp format const tsFormat = () => (new Date()).toLocaleTimeString() // Attach transports based on app mode if (process.env.APP_MODE === 'production') { // Log to file logger.add(new (winston.transports.DailyRotateFile)({ filename: path.join(__dirname, '../logs/errors-%DATE%.log'), datePattern: 'YYYY-MM-DD-HH', zippedArchive: true, format: winston.format.json(), handleExceptions: true })) } else { // Log to the console logger.add(new (winston.transports.Console)({ timestamp: tsFormat, colorize: true, handleExceptions: true })) } module.exports = logger También estoy usando Express y en mi middleware de manejo de errores, tengo este código:
const logger = require('../config/winston') function (err, req, res, next) { console.log(err) logger.error(err) res.status(500).send({ error: 'Please try again later.' }) } El problema es que cuando ocurre un error, todos los registros de winston son:
{"nivel":"error"}
Mientras que el viejo y bueno console.log() muestra:
TypeError: Cannot read property 'filename' of undefined at router.post (/Users/balazsvincze/Desktop/testapi/app/routes/upload.js:16:33) at Layer.handle [as handle_request] (/Users/de/Desktop/testapi/node_modules/express/lib/router/layer.js:95:5) at next (/Users/balazsvincze/Desktop/testapi/node_modules/express/lib/router/route.js:137:13) at Immediate.<anonymous> (/Users/balazsvincze/Desktop/testapi/node_modules/multer/lib/make-middleware.js:53:37) at runCallback (timers.js:814:20) at tryOnImmediate (timers.js:768:5) at processImmediate [as _immediateCallback] (timers.js:745:5) ¿Cómo hago para que winston registre algo como esto, incluido el seguimiento de la pila?
¡Muchas gracias!
EDITAR: si cambio la línea logger.error (err) a logger.error (err.message), al menos obtengo esto:
{"mensaje":"No se puede leer la propiedad 'nombre de archivo' de indefinido","nivel":"error"}
Todavía muy lejos de lo que busco.
Creo que lo que te estás perdiendo es format.errors({ stack: true }) en winston.createLogger .
const logger = winston.createLogger({ level: 'debug', format: format.combine( format.errors({ stack: true }), print, ), transports: [new transports.Console()], });Consulte este hilo de GitHub para obtener más información.
La razón por la que esto sucede es porque las interesantes propiedades del objeto Error , como .stack , no son enumerables. Algunas funciones verifican si sus parámetros son instancias de Error , como console.error , y otras funciones ignoran todas las propiedades no enumerables, como winston.<log-level> y JSON.stringify .
> console.error(new Error('foo')) Error: foo at repl:1:15 at Script.runInThisContext (vm.js:124:20) ...(abbr) > JSON.stringify(new Error('foo')) '{}'Dicho todo esto, es una usabilidad horrible tener un registrador de errores que esencialmente ignore los errores... Perdí demasiado tiempo con esto.
Una forma rápida y sucia sería registrar err.stack :
logger.error(err.stack); Un método más elaborado sería implementar un formato personalizado específicamente para instancias de Error . Hay un código de ejemplo sobre cómo implementar eso en este problema de Github .
Usando formato.
const { combine, timestamp, label, printf } = winston.format; const myFormat = printf(info => { if(info instanceof Error) { return `${info.timestamp} [${info.label}] ${info.level}: ${info.message} ${info.stack}`; } return `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`; }); winston.createLogger({ level: "info", format: combine( winston.format.splat(), label({ label: filename}), timestamp(), myFormat, ), transports: [ // // - Write to all logs with level `info` and below to `combined.log` // - Write all logs error (and below) to `error.log`. // new winston.transports.File({ filename: path.join(os.tmpdir(), "test", "test.log"), level: "info" }), ], });