這次寫Node.js項目的時候用了log模塊,遇到了壹個小問題。
這是壹個定期執行可配置自動化任務的項目,因此輸出信息會不斷增加,這意味著日誌文件會隨著時間的推移不斷增加。如果不控制日誌文件大小,服務器的磁盤遲早會滿。所以有必要限制文件大小。
理想的控制方法是當文件大小超過限制時,清除第壹個記錄的數據。類似於FIFO隊列。
#刪除以前的數據
- 1 xxx
......
100 abc
#在文件末尾追加數據
+ 101 xxxx
log4js的文件滾動
說到日誌,很多Node.js開發者肯定會找log4js。我們來看看log4js是如何處理這個問題的。
Log4js分為很多appenders(可以理解為記錄日誌的介質),文件滾動功能可以通過函數配置。
文件滾動功能有兩種方式:日期和文件大小。
要控制文件大小,當然選擇後者。
為了測試這個函數是否符合我們的要求,寫壹個循環代碼寫壹個日誌。
const log4js = require('log4js ')
//配置log4js
log4js.configure({
附錄:{
壹切:{
類型:“文件”,
文件名:' a.log ',
maxLogSize: 1000,
備份:0
},
},
類別:{
默認值:{
appenders: ['壹切'],
級別:“調試”
}
}
});
const log = log 4js . get logger();
for(設I = 0;我& lt41;i++) {
const str = i.toString()。padStart(6,' 000000 ');
log . debug(str);
}
執行後,會生成兩個文件a.log和a.log.1。
其中a.log.1有20行數據,實際大小是1kb,而a.log只有1行數據。
雖然它確實控制了文件大小,但它會帶來兩個問題:
會生成壹個額外的備份文件,所占用的總磁盤空間將超過文件限制。
日誌文件內容的大小是可變的,因此很可能需要結合備份文件來查詢日誌(例如,上面的情況日誌文件只有1行數據)。
推測log4js的實現邏輯可能如下:
檢查日誌文件是否達到限制大小,如果是,刪除備份文件,否則,繼續寫入日誌文件。
將日誌文件重命名為備份文件。
這顯然不能完全滿足需求。
字符串替換?
如果想在內存中完成循環覆蓋操作,相對簡單,使用string或Buffer即可。
添加字符串/緩沖區的長度,如果超過大小,則截取它。
寫入並覆蓋日誌文件。
但是有壹個很大的問題:占用內存。
例如,如果文件大小限制為1GB,同時寫入10個日誌文件,那麽至少會占用10GB的內存空間!
內存比磁盤空間更有價值,這麽明顯的性能問題顯然不是最好的解決方案。
文件卷
根據要求,實施步驟可分為兩步:
將最新數據追加到文件末尾。(Node.js的fs模塊有相應的函數)
刪除文件開頭超出限制的部分。(Node.js沒有響應函數)
這兩個步驟不分先後,但是Node.js並沒有提供刪除文件開頭的API,只是提供了修改文件指定位置的函數。
既然不能刪除文件開頭的內容,那就換個思路,只保留文件結尾的內容(大小限制內)。
什麽?這不是壹回事嗎?
略有不同~
刪除是對原始文件的操作,而保留內容可以借助臨時文件進行操作。
所以這個想法變成了:
創建壹個臨時文件,其內容來自日誌文件。
將數據添加到臨時文件中。
臨時文件中符合文件大小限制的內容從後向前讀取(以偏移量的形式),並復制到日誌文件中進行覆蓋。
為了不占用額外的磁盤空間,寫操作完成後會刪除臨時文件。
這樣就不會出現log4js這樣不完整的日誌文件,也不會保留額外的臨時文件。但是io的操作會增加~
寫操作可以通過tail命令實現,最終代碼如下:
私有寫(名稱:string,buf?:Buffer | string) {
//將buf追加到tmp文件
const tmpName = name.replace(/(。*\/)(.*$)/, '$1_\.$2\.tmp’);
如果(!existsSync(tmpName)) {
copyFileSync(名稱,tmpName);
}
buf & amp& ampappendFileSync(tmpName,buf);
//如果忙,請等待
如果(this.stream & amp& ampthis.stream.readable) {
this . needupdatelog file[name]= true;
}否則{
嘗試{
execSync(` tail-c $ { limit } $ { tmpName } & gt;$ { name } `);
嘗試{
if(this . needupdatelog file[name]){
this . needupdatelog file[name]= false;
this.write(姓名);
}否則{
existsSync(tmpName)和amp& ampunlink sync(tmpName);
}
} catch (e) {
控制臺.錯誤(e);
}
} catch (e) {
控制臺.錯誤(e);
}
}
}
摘要
完成這壹功能有兩種見解:
量變引起質變。當數據量變大的時候,很多簡單的處理方法就用不上了,比如寫文件。如果直接使用writeFile,會占用大量內存甚至內存可能不夠用。所以需要用合適的方式拆分,在拆分的過程中會遇到各種問題,比如本文中截取文件內容的要求。
學會借錢。君子本性不異,也善於作偽~在單點無法完成操作的時候,可以通過外部條件來實現,比如使用臨時文件來保存本文中的數據內容。
嗯,