From 03f25d1ee7f6a92642bd67d878b277009519e583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Cruz?= Date: Fri, 20 Dec 2024 13:41:33 +0000 Subject: [PATCH] Flush stdout and stderr before exiting --- README.md | 2 +- src/index.js | 14 ++++++++++++++ test/index.test.js | 11 +++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 10f583f..1592395 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ processManager.addHook({ ## Debug -Enable verbose debugging by configuring your own logger and passing it to `proccessManager.configure({ log: myCustomLogger })`. +Enable verbose debugging by configuring your own logger and passing it to `processManager.configure({ log: myCustomLogger })`. The minimum requirements for it to work is that the logger must be Object-like and have functions assigned to properties `info`, `warn`, and `error`. The functions should be able to handle two different argument signatures: diff --git a/src/index.js b/src/index.js index 7286ba5..30d3e2a 100644 --- a/src/index.js +++ b/src/index.js @@ -198,8 +198,22 @@ class ProcessManager { await this.hook('exit', this.errors); + this.log.info('Flushing output'); + + await this.flushOutput(); + this.exit(); } + + async flushOutput() { + // Process stdout and stderr can be in non-blocking mode so writes to it may not be flushed when the process exits. + // To ensure that all output is flushed before the process exits, we can write an empty string to stdout and stderr, + // and wait for the write operation to complete. + await Promise.all([ + new Promise(resolve => process.stdout.write('', resolve)), + new Promise(resolve => process.stderr.write('', resolve)) + ]); + } } /** diff --git a/test/index.test.js b/test/index.test.js index 224d5a7..e70ee10 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -19,6 +19,8 @@ describe('ProcessManager', () => { jest.spyOn(process, 'exit').mockImplementation(() => {}); jest.spyOn(process, 'on').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); + jest.spyOn(process.stderr, 'write').mockImplementation((data, cb) => cb?.()); + jest.spyOn(process.stdout, 'write').mockImplementation((data, cb) => cb?.()); const utils = require('../src/utils'); @@ -291,6 +293,15 @@ describe('ProcessManager', () => { expect(processManager.hook).toHaveBeenCalledWith('exit', []); }); + test('flushes stdout and stderr', async () => { + await processManager.shutdown(); + + expect(process.stdout.write).toHaveBeenCalledTimes(1); + expect(process.stdout.write).toHaveBeenCalledWith('', expect.any(Function)); + expect(process.stderr.write).toHaveBeenCalledTimes(1); + expect(process.stderr.write).toHaveBeenCalledWith('', expect.any(Function)); + }); + test('calls `processManager.exit()`', async () => { jest.spyOn(processManager, 'exit').mockImplementation(() => {});