在我當前的 FOSS Discord 機器人專案中,我有這個log.ts檔案來處理機器人的日志記錄。
它創建多個fs.WriteStream物件,這些物件寫入每個日志檔案。await log('CLOSE_STREAMS')當被呼叫以在每個 WriteStream 上運行函式時,代碼中有一段WriteStream#close(),回傳一個承諾。這在process.on('exit')處理程式中用于在我們關閉之前保存日志檔案。
這里的問題是'exit'事件處理程式無法將任何額外的作業安排到事件佇列中。
我如何處理呼叫 CLOSE_STREAMS 的方式,我可以按照我的預期運行這個退出處理程式?
功能實作,簡化
日志檔案log('CLOSE_STREAMS')
// Main
export default function log(mode: 'CLOSE_STREAMS'): Promise<void>;
export default function log(mode: 'v' | 'i' | 'w' | 'e', message: any, _bypassStackPrint?: boolean): void;
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export default function log(mode: 'v' | 'i' | 'w' | 'e' | 'CLOSE_STREAMS', message?: any, _bypassStackPrint = false): void | Promise<void> {
if (mode === 'CLOSE_STREAMS')
// Close all of the file streams
return new Promise((resolve) => {
errStr.end(() => {
warnStr.end(() => {
allStr.end(() => {
resolve();
});
});
});
});
else {
索引.ts
這是在未捕獲的例外中殺死日志的方式;這就是我想為退出事件做的事情。
// If we get an uncaught exception, close ASAP.
process.on('uncaughtException', async (error) => {
log('e', 'Killing client...', true);
client.destroy();
log('e', 'Client killed.', true);
log('e', 'Closing databases...', true);
client.closeDatabases();
log('e', 'Closed databases.', true);
log('e', 'An uncaught exception occured!', true);
log('e', `Error thrown was:`, true);
error.stack?.split('\n').forEach((item) => {
log('e', `${item}`, true);
});
log('e', 'Stack trace dump:', true);
let stack = new Error().stack?.split('\n');
stack?.shift();
if (!stack) stack = [];
stack.forEach((item) => {
log('e', `${item}`, true);
});
log('e', 'Process exiting.', true);
log('e', 'Exit code 5.', true);
log('e', 'Goodbye!', true);
await log('CLOSE_STREAMS'); // <<<<<<<<<<<< HERE
process.exit(5);
});
uj5u.com熱心網友回復:
如您所知,您不能在處理退出事件時可靠地使用異步操作,因為該行程將在它們完成之前退出。因此,我認為沒有任何方法可以使用 nodejs 流可靠地做到這一點。流具有完全異步的 API 和實作,包括重繪 和關閉。
在幾次谷歌搜索中,我發現其他一些人出于同樣的原因要求同樣的事情,但沒有提供解決方案。據我所知,這些是選項:
對流物件進行一些修改以向流中添加同步
flushAndClose()方法。您必須進入流的內部以獲取緩沖區和檔案句柄,并對任何剩余的緩沖區進行自己的同步寫入,然后對檔案句柄進行同步關閉。請注意,如果當前正在進行異步寫入操作,即使這樣也會有問題。放棄內置流,只實作自己的輕量級日志檔案介面,讓您輕松實作異步寫入(正常使用)和同步
flushAndClose()操作緊急關閉。請注意,如果您想要進行同步關閉時當前正在進行異步寫入操作,即使這樣也會出現問題。與其
process.on('exit', ...)用來觸發日志檔案的關閉,不如在鏈中上一層。無論是什么觸發了您的應用程式的關閉,將其放入一個異步函式中,該函式將在呼叫之前等待日志檔案正確關閉,process.exit()以便在您仍有能力處理異步操作時關閉日志檔案。從不同的(更穩定的)行程進行日志記錄。然后,該行程可以向該日志行程發送它想要記錄的訊息,并且該行程可以管理將日志資訊安全地存盤到磁盤,而與源行程是否突然關閉無關。
注意:退出行程將自動關閉所有打開的檔案選擇器(作業系統會為您處理)。因此,只要這是在某些致命錯誤條件下的邊緣情況關閉,而不是常見的正常關閉,那么也許您在這里沒有真正要解決的大問題。
可能發生的最糟糕的情況是,如果某些最近記錄的資料行尚未從流中重繪 ,您可能會丟失它們。請注意,流在可能的情況下會立即將資料寫入其描述符,因此它們通常不會累積大量緩沖資料。他們緩沖資料的時間是發生對流的新寫入,但之前的寫入仍在操作中。然后將要寫入的資料進行緩沖,直到先前的寫入操作完成。因此,資料永遠不會以空閑流留在緩沖區中。這往往會最大限度地減少(但不能消除)立即關閉時的資料丟失。
如果這是正常的常規關閉,那么您應該能夠使用上面的選項 #3 并重新調整關閉的發生方式,以便您可以在需要的地方使用異步代碼,以便正確關閉流。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/448902.html
上一篇:遞回和異步函式不尊重/等待承諾
下一篇:如何等待異步操作回圈?[復制]
