(logFilePath: string | undefined)
| 73 | // original console methods, and closes the file handle. Calling it |
| 74 | // is optional — the OS closes the fd on process exit anyway. |
| 75 | export function installLogging(logFilePath: string | undefined): () => void { |
| 76 | const file: FileLogger | undefined = |
| 77 | logFilePath !== undefined && logFilePath !== "" ? createFileLogger(logFilePath) : undefined; |
| 78 | // Save the original methods unbound so teardown can restore the |
| 79 | // exact same references the caller had pre-install. The wrappers |
| 80 | // call through bind() copies so they don't reenter themselves. |
| 81 | const originalLog = console.log; |
| 82 | const originalError = console.error; |
| 83 | const callLog = originalLog.bind(console); |
| 84 | const callError = originalError.bind(console); |
| 85 | const writeBoth = (level: LogLevel, original: (...args: unknown[]) => void) => { |
| 86 | return (...args: unknown[]): void => { |
| 87 | original(...args); |
| 88 | file?.write(level, args); |
| 89 | }; |
| 90 | }; |
| 91 | console.log = writeBoth("info", callLog); |
| 92 | console.error = writeBoth("error", callError); |
| 93 | |
| 94 | const onUncaught = (err: unknown): void => { |
| 95 | console.error("uncaughtException:", err); |
| 96 | process.exit(1); |
| 97 | }; |
| 98 | const onUnhandled = (reason: unknown): void => { |
| 99 | console.error("unhandledRejection:", reason); |
| 100 | process.exit(1); |
| 101 | }; |
| 102 | process.on("uncaughtException", onUncaught); |
| 103 | process.on("unhandledRejection", onUnhandled); |
| 104 | |
| 105 | return () => { |
| 106 | process.off("uncaughtException", onUncaught); |
| 107 | process.off("unhandledRejection", onUnhandled); |
| 108 | console.log = originalLog; |
| 109 | console.error = originalError; |
| 110 | file?.close(); |
| 111 | }; |
| 112 | } |
no test coverage detected