How to Log Full Stack Trace with Winston 3?

2020-05-20 00:48发布

问题:

My logger is set up like:

const myFormat = printf(info => {
   return `${info.timestamp}: ${info.level}: ${info.message}: ${info.err}`;
 });


 const logger =
   winston.createLogger({
   level: "info",
   format: combine(timestamp(), myFormat),

   transports: [
     new winston.transports.File({
     filename:
      "./logger/error.log",
        level: "error"
    }),
     new winston.transports.File({
       filename:
       "./logger/info.log",
       level: "info"
   })
  ]
})

Then I am logging out some error like this:

logger.error(`GET on /history`, { err });

How is it possible to log the full stack trace for errors to via the error transport? I tried passing in the err.stack and it came out as undefined.

Thanks !

回答1:

You can write a formatter to pass error.stack to log.

const errorStackFormat = winston.format(info => {
  if (info instanceof Error) {
    return Object.assign({}, info, {
      stack: info.stack,
      message: info.message
    })
  }
  return info
})

const logger = winston.createLogger({
  transports: [ ... ],
  format: winston.format.combine(errorStackFormat(), myFormat)
})

logger.info(new Error('yo')) // => {message: 'yo', stack: "Error blut at xxx.js:xx ......"} 

(the output will depend on your configuration)



回答2:

@Ming's answer got me partly there, but to have a string description with the error, this is how I got full stack tracing working on ours:

import winston from "winston";

const errorStackTracerFormat = winston.format(info => {
    if (info.meta && info.meta instanceof Error) {
        info.message = `${info.message} ${info.meta.stack}`;
    }
    return info;
});

const logger = winston.createLogger({
    format: winston.format.combine(
        winston.format.splat(), // Necessary to produce the 'meta' property
        errorStackTracerFormat(),
        winston.format.simple()
    )
});

logger.error("Does this work?", new Error("Yup!"));

// The log output:
//   error: Does this work? Error: Yup!
//       at Object.<anonymous> (/path/to/file.ts:18:33)
//       at ...
//       at ...


回答3:

For winston version 3.2.0+, following will add stacktrace to log output:

import { createLogger, format, transports } from 'winston';

const { combine, timestamp, prettyPrint, colorize, errors,  } = format;


const logger = createLogger({
  format: combine(
    errors({ stack: true }), // <-- use errors format
    colorize(),
    timestamp(),
    prettyPrint()
  ),
  transports: [new transports.Console()],
});  

Ref: https://github.com/winstonjs/winston/issues/1338#issuecomment-482784056



回答4:

Here is my logger.js with winston": "^3.1.0

const { createLogger, format, transports } = require('winston');
const { combine, timestamp, printf, colorize, splat } = format;

const myFormat = printf((info) => {
  if (info.meta && info.meta instanceof Error) {
    return `${info.timestamp} ${info.level} ${info.message} : ${info.meta.stack}`;
  }
  return `${info.timestamp} ${info.level}: ${info.message}`;
});

const LOG_LEVEL = process.env.LOG_LEVEL || 'debug';
const logger = createLogger({
  transports: [
    new (transports.Console)(
      {
        level: LOG_LEVEL,
        format: combine(
          colorize(),
          timestamp(),
          splat(),
          myFormat
        )
      }
    )
  ]
});
module.exports = logger;


回答5:

Here is my logger configuration. Added errors({ stack: true }), thanks to Murli Prajapati ans and small trick in printf function. My winston version is 3.2.1.

const {format, transports} = require('winston');
const { timestamp, colorize, printf, errors } = format;
const { Console, File } = transports;
LoggerConfig = {
        level: process.env.LOGGER_LEVEL || 'debug',
        transports: [
            new Console(),
            new File({filename: 'application.log'})
        ],
        format: format.combine(
            errors({ stack: true }),
            timestamp(),
            colorize(),
            printf(({ level, message, timestamp, stack }) => {
                if (stack) {
                    // print log trace 
                    return `${timestamp} ${level}: ${message} - ${stack}`;
                }
                return `${timestamp} ${level}: ${message}`;
            }),
        ),
        expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
        colorize: false, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
        ignoreRoute: function (req, res) {
            return false;
        } // optional: allows to skip some log messages based on request and/or response
}

I am using this same configuration in express-winston and for general log also.

const winston = require('winston');
const expressWinston = require('express-winston');

/**
 * winston.Logger
 * logger for specified log message like console.log
 */
global.__logger = winston.createLogger(LoggerConfig);
/**
 * logger for every HTTP request comes to app
 */
app.use(expressWinston.logger(LoggerConfig));



回答6:

Here is another take for Winston 3.2.

Nowadays Winston comes with a built-in stacktrace formatter, but it does not seem to trigger if the same formatter has winston.format.simple() combined. Thus, you need use winston.format.printf instead as by the answer from Kirai Mali. I could not figure out how to configure both winston.format.errors() and winston.format.simple() in the same configuration.

Based on the current Winston README example and answers above, here is my configuration that uses JSON format logfiles, but for the local development console it still gives colored log lines and good stack traces.


// Use JSON logging for log files
// Here winston.format.errors() just seem to work
// because there is no winston.format.simple()
const jsonLogFileFormat = winston.format.combine(
  winston.format.errors({ stack: true }),
  winston.format.timestamp(),
  winston.format.prettyPrint(),
);

// Create file loggers
const logger = winston.createLogger({
  level: 'debug',
  format: jsonLogFileFormat,
  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: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ],
  expressFormat: true,
});

// When running locally, write everything to the console
// with proper stacktraces enabled
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format:  winston.format.combine(
                winston.format.errors({ stack: true }),
                winston.format.colorize(),
                winston.format.printf(({ level, message, timestamp, stack }) => {
                  if (stack) {
                      // print log trace
                      return `${timestamp} ${level}: ${message} - ${stack}`;
                  }
                  return `${timestamp} ${level}: ${message}`;
              }),
            )
  }));
}