26

I want to flush the winston logger before process.exit.

process.on('uncaughtException', function(err){
    logger.error('Fatal uncaught exception crashed cluster', err);
    logger.flush(function(){ // <-
        process.exit(1);
    });
});

Is there anything like logger.flush available? I couldn't find anything about it, other than people complaining about winston not being very actively maintained.

As an alternative, is there any popular (actively maintained) multi-transport logging framework that provides a flushing capability?

bevacqua
  • 47,502
  • 56
  • 171
  • 285
  • I also have this problem with Winston - the file logger does not flush to disk when there is a fatal exception. – Thomas Bratt Nov 13 '13 at 12:19
  • 1
    Other people have given you a solution to your problem. But it seems that the answer to your actual question is no: there is no way to flush a winston logger. – Bennett McElwee Aug 04 '21 at 23:05

5 Answers5

15

Winston actually allows you to pass in a callback which is executed when all transports have been logged:

process.on('uncaughtException', function(err) {
    logger.log('error', 'Fatal uncaught exception crashed cluster', err, function(err, level, msg, meta) {
        process.exit(1);
    });
});

Docs: https://github.com/flatiron/winston#events-and-callbacks-in-winston

Thomas Heymann
  • 245
  • 3
  • 4
  • 4
    Correct, that lets me wait on the `'fatal exception'` message, what if I want to flush all of them? Maybe some other message was being logged and it's using a transport that takes longer to complete. – bevacqua Sep 13 '13 at 15:58
  • 2
    There will be nothing that is not flushed. The default winston FileTransport only buffers logs when attempting to open the underlying the file on the first attempt. https://github.com/flatiron/winston/blob/master/lib/winston/transports/file.js#L134-L142 Otherwise, we wait for the "drain" event if the data was not immediately written to underlying file descriptor: https://github.com/flatiron/winston/blob/master/lib/winston/transports/file.js#L158-L176 – indexzero Sep 13 '13 at 20:17
  • @indexzero say I log an error, which has a transport over HTTP or to store on a database, then I log a debug message which just goes to the console, and then wait on that one, and exit. Will the error message always get logged? – bevacqua Sep 16 '13 at 12:32
  • @indexzero If I set up a console logger and file logger, log some text and then quit with ctrl+c, the console logger displays all the logged text and the file logger only some of it. – Thomas Bratt Nov 15 '13 at 12:35
  • Really not clear whether FileTransport does or does not ever buffer its writes, following this comment trail. Is buffering only delegated to the file system? – matanster Jul 21 '14 at 14:56
  • [This may still lead to un-saved logged messages.](https://www.npmjs.com/package/winston-log-and-exit) – Jthorpe Jan 20 '17 at 18:44
3

Unfortuantely, Winston will sometimes call the logging callback before the transport has had a chance to flush, so the accepted answer can still lead to un-saved log messages (especially on the first turn of the event loop). A better solution is implemented in the winston-log-and-exit package / patch.

Jthorpe
  • 9,756
  • 2
  • 49
  • 64
1

Calling process.exit inside the log-callback like Thomas Heymann suggested will not ensure that the logs are actually flushed, especially when using a File-transport.

Instead of calling process.exit directly I would let the logger call process.exit after the log was flushed:

logger.js :

var winston = require('winston');

winston.loggers.add('my-logger', {
    console: {
        level: 'debug',
        colorize: true,
        timestamp: true,
        handleExceptions: true
    },
    file: {
        level: 'info',
        colorize: false,
        timestamp: true,
        filename: file,
        handleExceptions: true
    }
});

var logger = winston.loggers.get('my-logger');


/* ******* *
 * EXPORTS
 * ******* */

exports.exitAfterFlush = function(code) {
    logger.transports.file.on('flush', function() {
        process.exit(code);
    });
};

exports.info = function() {
    logger.info.apply(this, arguments);
};

exports.warn = function() {
    logger.info.apply(this, arguments);
};

exports.error = function() {
    logger.info.apply(this, arguments);
};

And in your code:

var logger = require('./logger.js');
logger.exitAfterFlush(0);
info('Done!');

Tested on NodeJS v4.1.2 and winston 1.1.0

Biggie
  • 7,037
  • 10
  • 33
  • 42
1

This may help someone. logger is an instance of winston.createLogger which defines two transports (console & file). The exit code is reflected in the shell.

const logger = require('../library/log');

function exitAfterFlush() {
    logger.on('finish', function () {
        logger.end();
    });
};
  
process.on('exit', (code) => {
    console.log(`About to exit with code: ${code}`);
});

logger.info(`Started with PID:${process.pid}`);
logger.error(`*****`);
process.exitCode = 11;
exitAfterFlush();
300baud
  • 540
  • 4
  • 17
-3

Winston has a better way to deal with this exceptions.

var logger = new (winston.Logger)({
    transports: [
      new winston.transports.File({ filename: 'path/to/all-logs.log' })
    ],
    handleExceptions: true,
    exceptionHandlers: [
      new winston.transports.File({ filename: 'path/to/exceptions.log' })
    ]
  });