Winston: Winston exception handler doesn't handle UnhandledPromiseRejectionWarning

Created on 18 Mar 2019  路  5Comments  路  Source: winstonjs/winston

Please tell us about your environment:

"devDependencies": {
"@types/chai": "^4.1.7",
"@types/chai-as-promised": "^7.1.0",
"@types/mocha": "^5.2.6",
"@types/mock-fs": "^3.6.30",
"@types/node": "^11.11.0",
"@types/sinon": "^7.0.9",
"@typescript-eslint/eslint-plugin": "^1.4.2",
"@typescript-eslint/parser": "^1.4.2",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"eslint": "^5.15.1",
"jest": "^24.4.0",
"mocha": "^6.0.2",
"mock-fs": "^4.8.0",
"sinon": "^7.2.7",
"ts-mockito": "^2.3.1",
"ts-node": "^8.0.3",
"typemoq": "^2.1.0",
"typescript": "^3.3.3333"
},
"dependencies": {
"@types/argparse": "^1.0.36",
"@types/fs-extra": "^5.0.5",
"@types/request-promise": "^4.1.42",
"argparse": "^1.0.10",
"fs-extra": "^7.0.1",
"moment": "^2.24.0",
"queue": "^6.0.0",
"request": "^2.88.0",
"request-promise": "^4.2.4",
"slack": "^11.0.2",
"winston": "^3.2.1"
}

What is the problem?

Throwing Error inside the async function is not handled by Winston exception handler, an error is not being logged so information is lost.

Other information

const _format = winston.format.printf((info) => `[${moment().format('YYYY/MM/DD hh:mm:ss')}] ${info.level}: ${info.message}`);
winston.createLogger({
        transports: [
          new winston.transports.File({ 
            filename: path.join(<string>EnvVars.get('ROOT_DIR'), 'logs', 'exceptions.log'), // valid path
            format: winston.format.combine(this._format),
            handleExceptions: true
          })
        ]
      })

(async function(){
    throw new Error('test') // not working, exceptions.log is empty
})()

(function(){
    throw new Error('test') // works
})()

Most helpful comment

I guess a workaround would be to use ->

process.on('unhandledRejection', (reason, promise) => {
        throw reason;
})

And from there Winston would pick up an exception and log it

All 5 comments

test.ts

import {createLogger, transports} from 'winston';

createLogger({
  transports: [
    new transports.File({ 
      filename: 'exception.log',
      handleExceptions: true
    })
   ]
});



async function main(): Promise<any>{
  throw new Error('test') // not working, exceptions.log is empty
}

function main1(): any{
  throw new Error('test') // works
}

main(); // doesn't work
main1(); // works

After compiling to js and running via cmd - only exception in non-promise type function is handled by winston

I guess a workaround would be to use ->

process.on('unhandledRejection', (reason, promise) => {
        throw reason;
})

And from there Winston would pick up an exception and log it

We added a RejectionHandler https://github.com/winstonjs/winston/blob/master/lib/winston/rejection-handler.js a few releases back -- I think using that will help with your issue! Might need both exception handler and rejection handler, depending on what kinds of issues you want to catch. Feel free to follow-up if that doesn't solve your issues.

It seems RejectionHandler class doesn't work as expected... I wasn't able to catch rejections with [email protected]

I guess private helper method _addHandler for handler addition was copied from exception handler without any modification.
You use the same flag handleExceptions here instead of handleRejections

/**
   * Helper method to add a transport as an exception handler.
   * @param {Transport} handler - The transport to add as an exception handler.
   * @returns {void}
   */
  _addHandler(handler) {
    if (!this.handlers.has(handler)) {
      handler.handleExceptions = true;
      const wrapper = new ExceptionStream(handler);
      this.handlers.set(handler, wrapper);
      this.logger.pipe(wrapper);
    }
  }

And after that proivate method _getRejectionHandlers filters assigned transports with handleRejections flag = true
see below:

 /**
   * Returns the list of transports and exceptionHandlers for this instance.
   * @returns {Array} - List of transports and exceptionHandlers for this
   * instance.
   * @private
   */
  _getRejectionHandlers() {
    // Remark (indexzero): since `logger.transports` returns all of the pipes
    // from the _readableState of the stream we actually get the join of the
    // explicit handlers and the implicit transports with
    // `handleRejections: true`
    return this.logger.transports.filter(wrap => {
      const transport = wrap.transport || wrap;
      return transport.handleRejections;
    });
  }

yes, the same issue happen on my project, should be a bug anyway

Was this page helpful?
0 / 5 - 0 ratings