Skip to content

Commit 229ecf1

Browse files
fix(test): eliminate Winston uncaughtException listener leak (#3544)
* fix(test): eliminate Winston uncaughtException listener leak in jest test runs Each jest.resetModules() call in beforeEach re-imports lib/services/logger.js, creating a new Winston logger with handleExceptions:true. Winston registers a new process.uncaughtException listener on every import but never removes orphaned listeners from prior module registry generations. Across 1226 tests with resetModules in beforeEach, this leaked ~570 listeners and ~285-1100MB of heap. The billing PRs (#3535-#3539) amplified an existing pre-billing leak (162 instances) by +409 new instances (+252%) — pushing trawl_node past its 6144MB cap. Fix: guard handleExceptions with `process.env.NODE_ENV !== 'test'` on both the Console transport (logger creation) and the File transport (setupFileLogger / getLogOptions). Exception handling is preserved in production and development. Also add `forceExit: true` to jest.config.js to prevent Jest from hanging on open MongoDB handles from integration test afterAll disconnect races. Verified: MaxListenersExceededWarning fully eliminated. 99 suites / 1226 tests all pass. * docs(logger): replace volatile metrics with stable explanation in handleExceptions guard
1 parent 9d39136 commit 229ecf1

2 files changed

Lines changed: 11 additions & 2 deletions

File tree

jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ export default {
162162
// The test environment that will be used for testing
163163
testEnvironment: 'node',
164164

165+
// Force Jest to exit after all tests complete — prevents hanging on open MongoDB
166+
// handles from integration tests whose afterAll disconnect races with Jest's exit
167+
forceExit: true,
168+
165169
// Global timeout for tests and hooks (integration tests bootstrap MongoDB + Express)
166170
testTimeout: 15000,
167171

lib/services/logger.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ const logger = new winston.createLogger({
2828
new winston.transports.Console({
2929
level: logLevel,
3030
format: consoleFormat,
31-
handleExceptions: true,
31+
// Disable exception handling in test env — each jest.resetModules() re-imports
32+
// this module, and handleExceptions:true registers a new process.uncaughtException
33+
// listener on every import without ever removing the old one, causing listener
34+
// accumulation and heap growth across module reloads in beforeEach hooks.
35+
handleExceptions: process.env.NODE_ENV !== 'test',
3236
}),
3337
],
3438
exitOnError: false,
@@ -71,7 +75,8 @@ const getLogOptions = () => {
7175
eol: '\n',
7276
tailable: true,
7377
showLevel: true,
74-
handleExceptions: true,
78+
// Same guard as the Console transport — see comment at top of createLogger call
79+
handleExceptions: process.env.NODE_ENV !== 'test',
7580
humanReadableUnhandledException: true,
7681
};
7782
};

0 commit comments

Comments
 (0)