node服務端開發中少不了日誌打點,而在koa框架下的日誌打點在多進程環境中日誌信息每每沒法對應上下文,並且在高併發下直接進行寫buffer操做(內核調用writev)也會形成內存泄漏,所以Midlog就是爲了緩解這種問題而產生的,其採用多種緩衝調度策略儘量下降writev的代價,減緩內存溢出的速率。node
日誌系統的三個關鍵要素:穩定、性能和易用。數組
當前log4js遇到的問題。緩存
設計新系統的前提。架構
回顧node原生模塊--「流」的讀與寫,涉及到了多個緩衝區以及他們緩衝區的實現(stream2.0的 Transform流同時包括了讀寫緩衝區)併發
優化點與優化方式。app
Midlog系統架構圖,其中reqContainer收集同一個上下文的日誌信息並轉碼;框架
StrategyMaker負責制定雙緩衝緩存策略和超時刷新以及強制刷新策略;koa
Writeable則負責真正的寫buffer操做,內部經過鏈表保存數據;函數
詳細介紹每一個模塊的功能。高併發
app.js
var koa = require('koa'); var midlog = require('midlog'); var app = koa(); // 配置日誌中間件 var firstValve = midlog({ env: 'online', exportGlobalLogger: true, appender: [{ type: 'INFO', logdir: '/tmp/log/midlog', pattern: '%d %r %x{name}:%z %p - %m%n', rollingFile: false, duation: 60000, name: 'info.log', nameformat: '[info.]HH-mm-ss[.log]', tokens: { name: 'helloworld' }, cacheSize: 5 * 1024 * 1024, flushTimeout: 15000 },{ type: 'ERROR', logdir: '/tmp/log/midlog', pattern: '%d %r %x{name}:%z %p - %m%n', rollingFile: false, duation: 60000, name: 'error.log', nameformat: '[info.]HH-mm-ss[.log]', tokens: { name: 'helloworld' }, cacheSize: 10240, flushTimeout: 10000 },{ type: 'TRACE', logdir: '/tmp/log/midlog', pattern: '%d %r %x{name}:%z %p - %m%n', rollingFile: false, duation: 60000, name: 'trace.log', nameformat: '[info.]HH-mm-ss[.log]', tokens: { name: 'helloworld' }, cacheSize: 5 * 1024 * 1024, flushTimeout: 10000 }] }); // 使用全局的logger接口 logger.info('i am the global logger'); // 將midlog放在中間件的前列 app.use(firstValve); // 業務中間件 app.use(function*(next){ this.logger.info(this.url+' this is the first valve!! '); this.logger.error('midlog tracing' + this.url+' this is the first valve!! '); this.logger.trace('midlog tracing' + this.url+' this is the first valve!! '); yield next; }); app.use(function*(){ this.logger.info(this.url+' this is the 2cd valve!! '); this.logger.error('midlog tracing' + this.url+' this is the 2cd valve!! '); this.logger.trace('midlog tracing' + this.url+' this is the 2cd valve!!'); this.body = '<h1>hello midlog</h1>'; }); app.listen(8888);
midlog提供了3種日誌刷新級別:
TRACE、INFO、ERROR,
而且提供了兩種寫日誌文件的方式:
單文件寫 (經過設置appender的rollingFile爲false觸發)
文件分時間片寫 (經過設置appender的rollingFile爲true觸發)
midlog採用和log4js相同的layout格式和語法,生成可定製的日誌輸出格式。
最後,midlog採用多級緩衝的架構(針對單文件寫模式採用雙緩衝,文件分時寫模式採用單緩衝),能夠有效的控制Stream寫的頻率,而緩衝的大小和刷新頻率能夠由開發者根據實際須要自由設置。
env {String} 環境設置。若設置爲development,則會在控制檯和文件中同時輸出日誌
exportGlobalLogger {Boolean} 是否保留全局logger對象。設置爲true,則在全局使用logger對象
appender {Array} 日誌類型配置數組。數組每一項描述每種類型日誌的相關信息及緩衝刷新頻率
type {String} 日誌類型。能夠爲 「INFO、TRACE和ERROR」 任意一種
logdir {String} 日誌文件所在的絕對目錄
rollingFile {Boolean} 是否按照時間進行日誌文件分割。設置爲true時則按照設置的duration間隔分割文件
duration {Number} 分割日誌文件的間隔。若rollingFile爲true,則按照duration大小分割文件
name {String} 日誌文件名稱。name屬性在單文件寫模式下有效,在rollingFile == true時無效
'd': 日期和時間, 'h': 主機名稱, 'm': 日誌信息格式化,主要優化錯誤輸出, 'n': 換行符, 'p': 日誌級別, 'r': 時間輸出, 'z': 進程號輸出, '%': 百分號佔位符, 'x': 用戶自定義變量或函數,搭配{token}屬性
如定義nameformat爲 pattern: '%d %r %x{name}:%z %p - %m%n'
且tokens設置爲 {name: 'helloworld'}
則輸出日誌格式爲:
(%d) (%r) (%x{name}) (%z) (%p) (%m) (%n) 2017-01-16 10:59:55.611 10:59:55 helloworld:13736 INFO - / this is the first valve!!
cacheSize {Number} 緩衝大小,單位字節。midlog在單文件寫模式下采用雙緩衝結構控制I/O速率,所以開發者能夠經過定義緩衝大小實現高效的寫入流程,默認爲10kB大小;在文件分時間片寫模式下該選項無效
flushTimeout {Number} 緩衝刷新間隔。在單文件寫和文件分時間片寫兩種模式下都起做用,定點刷新緩衝