為Express.js編寫一個Logger

peacentury 8年前發布 | 9K 次閱讀 Express JavaScript開發 Express.js

Express.js 是Node.js下最基礎最靈活的Web服務器。 Express的日志工具有很多,比如默認的訪問日志工具 morgan , 通用日志工具 winston 等等。

本文便來發掘一下這些日志工具的優秀特性,并一一給出實現: 對象輸出、日期前綴、訪問日志,模塊名前綴,以及彩色輸出等。

JSON Stringify

JavaScript服務器輸出JSON真是再平常不過了,輸出可讀的JSON在開發中非常有用。 為了輸出可讀的JSON,我們將所有對象類型的參數 stringify 即可。

function log(){
    var args = Array.prototype.slice.call(arguments).map(stringify);
    console.log.apply(console, args);
}
function stringify(arg) {
    return typeof arg === 'object' ?
        JSON.stringify(arg, null, 4) : arg;
}
log({foo: ["bar", "foo"]});
// 輸出:
// {
//     foo: ["bar", "foo"]
// }

我們希望 log 函數像 console.log 一樣接受多個參數,因此我們需要對所有參數進行map。

日期前綴

當日志用于服務器環境時,我們希望知道日志的輸出時間, 這在性能調試和調試因果關系時非常重要。 尤其是在使用像pm2這樣的進程監視器時,錯誤和標準輸出是分開存儲的。 如果沒有時間戳很難得知順序關系。

function parse(str) {
    // 為每行都添加時間戳
    return str.split('\n')
        .map(prefixify)
        .join('\n');
}

function prefixify(str){
    var now = new Date();
    var prefix = `[${now.toLocaleString()}]`;
    // 將時間戳放在行首
    return prefix + ' ' + str;
}

時間日期的格式化可以使用 strftime

提供類console對象

我們的logger的使用方式最好與console相同以獲得最好的兼容與可用性。 于是需要提供 .log() , .warn() , .error() , .info() 等方法, 同時也需要支持多參數的情形。我們需要做的是封裝所有的 console 方法:

function parse(argvs) {
    return Array.prototype.slice.call(argvs)
        .map(stringify).join(' ')
        .split('\n').map(prefixify).join('\n');
}
function prefixify(str){
    var now = new Date();
    var prefix = `[${now.toLocaleString()}]`;
    return prefix + ' ' + str;
}
function stringify(arg) {
    return typeof arg === 'object' ?
        JSON.stringify(arg, null, 4) : arg;
}
module.exports = {
    log:   function(){ console.log(parse(arguments)) },
    warn:  function(){ console.warn(parse(arguments)) },
    info:  function(){ console.info(parse(arguments)) },
    error: function(){ console.error(parse(arguments)) }
};

彩色的輸出

在開發環境中輸出彩色的日志可以讓我們更快地獲取信息,在終端中輸出彩色需要使用特殊字符。 自定義過 PS1 的童鞋一定會感受到手寫這些字符的費勁,在Node.js中我們可以使用 colors 庫來完成這件事情。

npm install colors

然后在輸出時便可以先調用 colors 提供的API做字符串轉換,現在來把日期前綴變成青色的:

const colors = require('colors/safe');

function prefixify(str){
    var now = new Date();
    var prefix = `[${now.toLocaleString()}]`;
    // 將時間戳放在行首
    return colors.cyan(prefix) + ' ' + str;
}

帶顏色的輸出只是具有特殊字符的字符串,可以像普通字符串一樣進行操作。

Express訪問日志

現在我們Express訪問日志與普通日志格式一致,這需要監聽Express請求和響應。 需要用到 on-headers 來監聽寫Response Header事件。

const onHeaders = require('on-headers');
// 訪問日志
app.use(function(req, res, next) {
    req.receivedAt = Date.now();
    onHeaders(res, function() {
        var duration = Date.now() - req.receivedAt;
        // 這里調用我們的logger
        // 示例輸出: [2016-07-28 10:23:02] GET / 200 21ms
        logger.log(req.method.toUpperCase(),
            req.originalUrl,
            res.statusCode,
            duration + 'ms');
    });
    next();
});
// use 路由

可能你發現Express的默認訪問日志工具 morgan 會輸出訪問耗時。 這需要同時封裝 console.time和 console.timeEnd`,本文就不贅述了。

模塊名綁定

標準的Logger大多可以綁定一個模塊名(或者Trace ID), 模塊名會在該模塊的每條日志的前綴部分出現,以方便跟蹤Log是哪個模塊(或業務線)輸出的。 其用法大致如下:

var logger = Logger('account:factory');
logger.log('create account error');
// 輸出:
// [account:factory] create account error

如何實現呢?來一個簡單的閉包即可:

function Logger(traceId){
    return {
        log: function(str){
            console.log(`[${traceId}] ${str}`);
        }
    }
}

你還可以將 [account:factory] 顯示為灰色,并支持多參數的輸出(見上文)。

 

來自:http://harttle.com/2016/09/22/express-logger.html

 

 本文由用戶 peacentury 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!