log4js 是Node.js 日誌處理中的數一數二的模塊。比起console 或者TJ 的debug 有其優勢,尤其針對投入生產的Node.js 項目來說下面這些是不可少的:
本文將會給你一個log4js 的全面介紹,讓你可以在項目中駕輕就熟的使用log4js,開發調試容易,線上更好地監控或排查問題。
牛刀小試
下面這三行代碼為你展示了log4js 最簡單的用法:
// file: simplest.jsvar log4js = require('log4js');var logger = log4js.getLogger();logger.debug("Time:", new Date());調用.getLogger() 可以獲得log4js 的Logger 實例,這個實例的用法與console 是一致的,可以調用.debug (也有.info 、 .error 等方法)來輸出日誌。
運行node simplest.js ,輸出如下:
$node simplest.js[2016-08-21 00:01:24.852] [DEBUG] [default] - Time: 2016-08-20T16:01:24.852Z
Time: 2016-08-20T16:01:24.852Z 是我們想要輸出的內容,前面的包含說明符[2016-08-21 00:01:24.852] [DEBUG] [default] 後文再表。
使用起來是不是也很簡單,好了,在我們深入到log4js 高級用法之前,我們先來熟悉一下幾個log4js 中的概念。
Level
這個理解起來不難,就是日誌的分級。日誌有了分級,log4js 才能更好地為我們展示日誌(不同級別的日誌在控制台中採用不同的顏色,比如error 通常是紅色的),在生產可以有選擇的落盤日誌,比如避免一些屬於.debug 才用的敏感信息被洩露出來。
log4js 的日誌分為九個等級,各個級別的名字和權重如下:
{ ALL: new Level(Number.MIN_VALUE, "ALL"), TRACE: new Level(5000, "TRACE"), DEBUG: new Level(10000, "DEBUG"), INFO: new Level(20000, "INFO"), WARN: new Level(30000, "WARN"), ERROR: new Level(40000, "ERROR"), FATAL: new Level(50000, "FATAL"), MARK: new Level(9007199254740992, "MARK"), // 2^53 OFF: new Level(Number.MAX_VALUE, "OFF")}上個圖:
ALL OFF 這兩個等級並不會直接在業務代碼中使用。剩下的七個即分別對應Logger 實例的七個方法, .trace .debug .info ... 。也就是說,你在調用這些方法的時候,就相當於為這些日誌定了級。因此,之前的[2016-08-21 00:01:24.852] [DEBUG] [default] - Time: 2016-08-20T16:01:24.852Z 中的DEBUG 既是這條日誌的級別。
類型
log4js 還有一個概念就是category(類型),你可以設置一個Logger 實例的類型,按照另外一個維度來區分日誌:
// file: set-catetory.jsvar log4js = require('log4js');var logger = log4js.getLogger('example');logger.debug("Time:", new Date());在通過getLogger 獲取Logger 實例時,唯一可以傳的一個參數就是loggerCategory(如'example' ),通過這個參數來指定Logger 實例屬於哪個類別。這與TJ 的debug 是一樣的:
var debug = require('debug')('worker');setInterval(function(){ debug('doing some work');}, 1000);在debug 中'worker' ,同樣也是為日誌分類。好了,回來運行node set-catetory.js :
[2016-08-21 01:16:00.212] [DEBUG] example - Time: 2016-08-20T17:16:00.212Z
與之前的[2016-08-21 00:01:24.852] [DEBUG] [default] - Time: 2016-08-20T16:01:24.852Z 唯一不同的地方就在於, [default] 變成了example 。
那類別有什麼用呢,它比級別更為靈活,為日誌了提供了第二個區分的維度,例如,你可以為每個文件設置不同的category,比如在set-catetory.js 中:
// file: set-catetory.jsvar log4js = require('log4js');var logger = log4js.getLogger('set-catetory.js');logger.debug("Time:", new Date());就可以從日誌[2016-08-21 01:24:07.332] [DEBUG] set-catetory.js - Time: 2016-08-20T17:24:07.331Z 看出,這條日誌來自於set-catetory.js 文件。又或者針對不同的node package 使用不同的category,這樣可以區分日誌來源於哪個模塊。
Appender
好了,現在日誌有了級別和類別,解決了日誌在入口處定級和分類問題,而在log4js 中,日誌的出口問題(即日誌輸出到哪裡)就由Appender 來解決。
默認appender
下面是log4js 內部默認的appender 設置:
// log4js.jsdefaultConfig = { appenders: [{ type: "console" }]}可以看到,在沒有對log4js 進行任何配置的時候,默認將日誌都輸出到了控制台。
設置自己的appender
我們可以通過log4js.configure 來設置我們想要的appender。
// file: custom-appender.jsvar log4js = require('log4js');log4js.configure({ appenders: [{ type: 'file', filename: 'default.log' }]})var logger = log4js.getLogger('custom-appender');logger.debug("Time:", new Date());在上例中,我們將日誌輸出到了文件中,運行代碼,log4js 在當前目錄創建了一個名為default.log 文件, [2016-08-21 08:43:21.272] [DEBUG] custom-appender - Time: 2016-08-21T00:43:21.272Z 輸出到了該文件中。
log4js 提供的appender
Console 和File 都是log4js 提供的appender,除此之外還有:
DateFile:日誌輸出到文件,日誌文件可以安特定的日期模式滾動,例如今天輸出到default-2016-08-21.log ,明天輸出到default-2016-08-22.log ;
SMTP:輸出日誌到郵件;
Mailgun:通過Mailgun API 輸出日誌到Mailgun;
levelFilter 可以通過level 過濾;
等等其他一些appender,到這裡可以看到全部的列表。
過濾級別和類別
我們可以調整appender 的配置,對日誌的級別和類別進行過濾:
// file: level-and-category.jsvar log4js = require('log4js');log4js.configure({ appenders: [{ type: 'logLevelFilter', level: 'DEBUG', category: 'category1', appender: { type: 'file', filename: 'default.log' } }]})var logger1 = log4js.getLogger('category1');var logger2 = log4js.getLogger('category2');logger1.debug("Time:", new Date());logger1.trace("Time:", new Date());logger2.debug("Time:", new Date());運行,在default.log 中增加了一條日誌:
[2016-08-21 10:08:21.630] [DEBUG] category1 - Time: 2016-08-21T02:08:21.629Z
來看一下代碼:
使用logLevelFilter 和level 來對日誌的級別進行過濾,所有權重大於或者等於DEBUG 的日誌將會輸出。這也是之前提到的日誌級別權重的意義;
通過category 來選擇要輸出日誌的類別, category2 下面的日誌被過濾掉了,該配置也接受一個數組,例如['category1', 'category2'] ,這樣配置兩個類別的日誌都將輸出到文件中。
Layout
Layout 是log4js 提供的高級功能,通過layout 我們可以自定義每一條輸出日誌的格式。 log4js 內置了四中類型的格式:
messagePassThrough:僅僅輸出日誌的內容;
basic:在日誌的內容前面會加上時間、日誌的級別和類別,通常日誌的默認layout;
colored/coloured:在basic 的基礎上給日誌加上顏色,appender Console 默認使用的就是這個layout;
pattern:這是一種特殊類型,可以通過它來定義任何你想要的格式。
一個pattern 的例子:
// file: layout-pattern.jsvar log4js = require('log4js');log4js.configure({ appenders: [{ type: 'console', layout: { type: 'pattern', pattern: '[%r] [%[%5.5p%]] - %m%n' } }]})var logger = log4js.getLogger('layout-pattern');logger.debug("Time:", new Date());%r %p $m $n 是log4js 內置的包含說明符,可以藉此來輸出一些meta 的信息,更多細節,可以參考log4js 的文檔。
一張圖再來說明一下,Logger、Appender 和Layout 的定位。
實戰:輸出Node 應用的ACCESS 日誌access.log
為了方便查問題,在生產環境中往往會記錄應用請求進出的日誌。那使用log4js 怎麼實現呢,直接上代碼:
// file: server.jsvar log4js = require('log4js');var express = require('express');log4js.configure({ appenders: [{ type: 'DateFile', filename: 'access.log', pattern: '-yyyy-MM-dd.log', alwaysIncludePattern: true, category: 'access' }]});var app = express();app.use(log4js.connectLogger(log4js.getLogger('access'), { level: log4js.levels.INFO }));app.get('/', function(req,res) { res.send('前端外刊評論');});app.listen(5000);看看我們做了哪些事情:
配置了一個appender,從日誌中選出類別為access 的日誌,輸出到一個滾動的文件中;
log4js.getLogger('access') 獲取一個類別為access 的Logger 實例,傳遞給log4js.connectLogger 中間件,這個中間件收集訪問信息,通過這個實例打出。
啟動服務器,訪問http://localhost:5000,你會發現目錄中多了一個名為access.log-2016-08-21.log 的文件,裡面有兩條日誌:
[2016-08-21 14:34:04.752] [INFO] access - ::1 - - "GET / HTTP/1.1" 200 18 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
[2016-08-21 14:34:05.002] [INFO] access - ::1 - - "GET /favicon.ico HTTP/1.1" 404 24 "http://localhost:5000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
通過log4js 日誌的分類和appender功能,我們把訪問日誌輸出到了一個滾動更新的文件之中。
總結
本文為大家全面地介紹了log4js 的用法,與console 或者簡單的日誌工具相比,log4js 使用起來更複雜,當然功能更強大,適合生產級應用的使用。如果大家有興趣的話,請留言告訴外刊君,接下來可能為大家介紹如何在Node 應用中做配置管理。