1. 程式人生 > >nodejs之日誌管理

nodejs之日誌管理

好玩的 kit path tool 圖片 .json https express cluster

開發一個項目時,可以通過控制臺輸出或者debug來獲取到項目的運行信息。當項目上線時,我們就需要通過日誌來分析。如同Java的log4j,nodejs中也有相關的log4js。使用過log4j的同學應該對此不會陌生。

1、日誌級別

log4js共有6種日誌級別,分別為:trace、debug、info、warn、error、fatal。權值從小到大,其初始化代碼為:

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"),

假如設置默認的日誌級別為info,那麽權值小於info的日誌不會被記錄下來,也就是說只有調用log.info(), log.warn(), log.error()或者log.fatal()才會觸發記錄日誌。該部分代碼在lib/logger.js中。

技術分享圖片
Logger.prototype.log = function() {
  var args = Array.prototype.slice.call(arguments)
  , logLevel = levels.toLevel(args.shift())
  , loggingEvent;
  if (this.isLevelEnabled(logLevel)) {
    loggingEvent = new LoggingEvent(this.category, logLevel, args, this);
    this.emit("log", loggingEvent);
  }
};
技術分享圖片

2、集成express

log4js可以作為express的一個中間件來使用。首先需要引入log4js

var express = require("express");
var log4js = require("log4js");

var app = express();

接著配置log4js

log4js.configure({
 appenders: [
   { type: ‘console‘ },
   { type: ‘file‘, filename: ‘cheese.log‘, category: ‘cheese‘ }
  ]
});

該配置的意思是console是默認的appender,使用cheese這個appender時會將日誌記錄文件中,日誌文件名為cheese.log。

然後用use連接到中間件,我們默認使用的是cheese這個appender,級別為info。

app.use(log4js.connectLogger(log4js.getLogger("cheese"), {level: log4js.levels.INFO}));

其輸出與此類似:

[2014-07-04 20:27:21.205] [INFO] cheese - 127.0.0.1 - - "GET / HTTP/1.1" 200 22896 "" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36"

......

3、再修改一下中間件

上面的做法是可以的,只是當中間件太多的時候,都寫在同一個文件中也許感覺會有些醜陋。所以我傾向於將其分離出來作為單獨的一個模塊,也就是一個單獨的文件。然後對外暴露接口。

技術分享圖片
var path = require("path");
var log4js = require("log4js");


/**
 * 日誌配置
 */
exports.configure = function() {
    log4js.configure(path.join(__dirname, "log4js.json"));    
}

/**
 * 暴露到應用的日誌接口,調用該方法前必須確保已經configure過
 * @param name 指定log4js配置文件中的category。依此找到對應的appender。
 *              如果appender沒有寫上category,則為默認的category。可以有多個
 * @returns {Logger}
 */
exports.logger = function(name) {
    var dateFileLog = log4js.getLogger(name);
    dateFileLog.setLevel(log4js.levels.INFO);
    return dateFileLog;
}

/**
 * 用於express中間件,調用該方法前必須確保已經configure過
 * @returns {Function|*}
 */
exports.useLog = function() {
    return log4js.connectLogger(log4js.getLogger("app"), {level: log4js.levels.INFO});
} 
技術分享圖片

log4js.json文件內容如下

技術分享圖片
{
    "appenders": [
        {
            "type": "console"
        },
        {
            "type": "dateFile",
            "filename": "logs/booklist.log",
            "pattern": "-yyyy-MM-dd",
            "alwaysIncludePattern": true
        }
    ]
}
技術分享圖片

配置很簡單,配置了兩個appender,一個是控制臺的,一個是dateFile,意思是每天都產生一個日誌文件。註意到我這裏並沒有配置category,這樣的話當沒有找到對應的appender時,這兩個appender就是默認的appender。有些時候明明感覺配置沒有錯,但是日誌文件並沒有產生日誌,往往問題就出在這裏。

然後在app.js中我們修改為如下

技術分享圖片
var express = require("express");
// 這個是我們上面自定義的模塊
var log4js = require("./log");

var app = express();
app.configure();

app.use(log4js.useLog());

...
技術分享圖片

4、單進程與多進程

好了,上面對於單進程是適用的,但是如果你的nodejs應用是多進程的,使用上面的配置你會看到日誌的輸出有點奇怪,比如:

技術分享圖片

感覺有點像是資源搶占了。

log4js的wiki中有給出multiprocess的配置。但是當時使用的時候也會有問題,當時沒有細究。不過社區中有人建立了另外一種方式,我采用了這種。可以參看一下這個issue。下面我們來配置一下。修改我們在上面修改的log模塊文件,變為:

技術分享圖片
var path = require("path");
var log4js = require("log4js");


/**
 * 多進程的日誌配置
 */
exports.configure = function(mode) {
    if (mode === "master") {
        log4js.configure(path.join(__dirname, "./log4js-master.json"));
    } else {
        // 多進程的配置項
        log4js.configure(path.join(__dirname, "./log4js-worker.json"));
        // 單進程的配置項
//        log4js.configure(path.join(__dirname, "../config/log4js.json"));
    }
}

/**
 * 暴露到應用的日誌接口,調用該方法前必須確保已經configure過
 * @param name 指定log4js配置文件中的category。依此找到對應的appender。
 *              如果appender沒有寫上category,則為默認的category。可以有多個
 * @returns {Logger}
 */
exports.logger = function(name) {
    var dateFileLog = log4js.getLogger(name);
    dateFileLog.setLevel(log4js.levels.INFO);
    return dateFileLog;
}

/**
 * 用於express中間件,調用該方法前必須確保已經configure過
 * @returns {Function|*}
 */
exports.useLog = function() {
    return log4js.connectLogger(log4js.getLogger("app"), {level: log4js.levels.INFO});
}
技術分享圖片

主要是修改了configure方法。

log4js-master.json的內容為:

技術分享圖片
{
    "appenders": [{
        "type": "clustered",
        "appenders": [
            {
                "type": "console"
            },
            {
                "type": "dateFile",
                "filename": "logs/booklist.log",
                "pattern": "-yyyy-MM-dd",
                "alwaysIncludePattern": true,
                "pollInterval": 1,
                "category": "dateFileLog"
            }
        ]
    }]
}
技術分享圖片

log4js-worker.js的內容為:

{
    "appenders": [{
        "type": "clustered"
    }]
}

假設主進程的內容在文件master.js,工作進程在worker.js。master.js中的配置內容為:

var log4js = require("./lib/log");
log4js.configure("master");

worker.js的配置內容為:

技術分享圖片
var express = require("express");
// 這個是我們上面自定義的模塊
var log4js = require("./log");

var app = express();
app.configure("worker");

app.use(log4js.useLog());

...
技術分享圖片

如此就完成了。

需要在某個地方記錄日誌的時候,我們可以如此

var log = require("./log").logger("index"); // logger中的參數隨便起

...
log.info("..."); log.error("...")

5、寫在後面

log4js還有挺多好玩的內容,比如smtp。這是個很有用的功能,比如當項目發生某個錯誤時,你希望程序能發郵件通知你,該功能就能派出用場了。

另外,我開源了一個項目booklist。該項目主要在於探討nodejs建立應用所需要或者註意的地方,非常期待各位指導與探討,謝謝!

nodejs之日誌管理