Winston: logger.close & transport.close should return promises

Created on 21 Apr 2020  路  1Comment  路  Source: winstonjs/winston

Please tell us about your environment:

  • _winston version?_ ^3.2.1

    • [ ] winston@2

    • [x] winston@3

  • _node -v outputs:_ v12.16.2
  • _Operating System?_ (Windows, macOS, or Linux) macOS
  • _Language?_ (all | TypeScript X.X | ES6/7 | ES5 | Dart) TypeScript 3.8.3

What is the problem?

I have a seeding script that runs locally and in builds. It relies on a winston logger that can be configured with multiple transports, including a remote sink over UDP (papertrail), and a daily rotate file transport. When the main function completes and is done with the logger the last line executes logger.close(). The code hangs waiting for the transports to close.

It's very important for the seeding script to exit cleanly and accurately. The build should fail if there is a seeding error - which is based on the return code.

Also: I could not find any clear documentation on how to shut down your logger (and it's transports). I spent a lot of time today dissecting winston code to figure out what is happening.

Lacking a contract for shutting down, each Transport handles things differently.

  • The daily-rotate-file transport emits 'finish'
  • The SysLog transport emits 'closed'

My workaround below takes advantage of this, but since it relies on the implementation detail and not a contract, the code could break when we upgrade winston.

What do you expect to happen instead?

  • The contract for logger.close() and Transport.close should each return a promise.
  • logger.close should invoke close on each transport (which it does) ...and then return a Promise.all which resolves when every transport's returned close promise has resolved. Which it doesn't.
  • Establish clear documentation on how to shut down a Logger, including it's transports.

Other information

Here is a workaround... the winston logger is wrapped in a nestjs LoggerService

LoggerService Snippet

  async close() {
    const promises: Promise<any>[] = [];

    // close all transports -- transports dont use promises...
    // syslog close function emits 'closed' when done
    // daily-rotate-file close function emits 'finish' when done
    for (const transport of this.logger.transports) {
      if (transport.close) {
        const promise = new Promise(resolve => {
          transport.once('closed', () => {
            resolve();
          });
          transport.once('finish', () => {
            resolve();
          });
        });
        promises.push(promise);
        // transport.close();  <-- invoked by logger.close()
      }
    }

    this.logger.close();
    return Promise.all(promises);
  }

Calling Code Snippet:

    try {
      await logger.close();
    } catch (err) {
      console.log(err);
      process.exit(1);
    }
    process.exit(0);

Most helpful comment

Thank you for posting your close() function - it works like a charm for me (also in a wrapper with a console, daily-rotate-file, and custom sentry transport). 馃憤

Waiting for a log to close is basic and the lack of these contracts is a weakness that makes the library difficult to deal with, so thanks for posting your workaround.

>All comments

Thank you for posting your close() function - it works like a charm for me (also in a wrapper with a console, daily-rotate-file, and custom sentry transport). 馃憤

Waiting for a log to close is basic and the lack of these contracts is a weakness that makes the library difficult to deal with, so thanks for posting your workaround.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pocesar picture pocesar  路  3Comments

kjin picture kjin  路  3Comments

mohanen picture mohanen  路  4Comments

sinai-doron picture sinai-doron  路  3Comments

KingRial picture KingRial  路  3Comments