nodejs-日志收集

開發(fā)過程中,日志記錄是必不可少的事情,尤其是生產(chǎn)系統(tǒng)中經(jīng)常無法調(diào)試,因此日志就成了重要的調(diào)試信息來源。

1.expressWinston

訪問日志一般用來記錄每個客戶端對應(yīng)用的訪問請求。在Web應(yīng)用中,主要記錄HTTP請求中的關(guān)鍵數(shù)據(jù)。如下所示記錄:

{
    "res":{
        "statusCode":200
    },
    "req":{
        "url":"/user/login",
        "headers":{
            "host":"localhost:8001",
            "connection":"keep-alive",
            "content-length":"348",
            "postman-token":"0ef999d7-1b51-4bc5-c4d8-f067f165b2be",
            "cache-control":"no-cache",
            "origin":"chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop",
            "user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
            "content-type":"multipart/form-data; boundary=----WebKitFormBoundaryef7X45UMFFjdZwsB",
            "accept":"*/*",
            "dnt":"1",
            "accept-encoding":"gzip, deflate, br",
            "accept-language":"zh-CN,zh;q=0.8,en;q=0.6",
            "cookie":"SID=s%3AzPVGIcZLGk9osGdrAOpF9BULNZJnGwkj.INJURLoEcBKNFZlvCNonwpqFJq56lXlpQFIu15c6N1Y"
        },
        "method":"POST",
        "httpVersion":"1.1",
        "originalUrl":"/user/login",
        "query":{

        }
    },
    "responseTime":39,
    "level":"info",
    "message":"HTTP POST /user/login",
    "timestamp":"2017-09-12T08:25:44.568Z"
}

中間件winston express-winston提供了請求的完整記錄,記錄的內(nèi)容有:

  • res結(jié)果
  • req請求包含:url, headlers, method, httpVersion, originalUrl, query六大項
  • responseTime相應(yīng)時間
  • level當(dāng)前日志等級
  • message 請求基本信息
  • timestamp 時間戳

其中對于日志中是否包含哪些信息,中間件也做了比較好的支持:

expressWinston.requestWhitelist.push('body');
expressWinston.responseWhitelist.push('body');

參考:express-winston

記錄日志的目的是為了分析,當(dāng)前的這些數(shù)據(jù)已經(jīng)足夠用來幫助分析Web應(yīng)用的用戶分布情況、服務(wù)器端的相應(yīng)時間、相應(yīng)狀態(tài)和客戶端類型等。通過這些數(shù)據(jù)反過來幫助我們改進(jìn)和提升網(wǎng)站。在nodejs中可以使用express-winston將日志打印在控制臺,也可以將其打印在本地文件中,還可以將其寫入至數(shù)據(jù)庫中。寫法如下:

import winston from 'winston';
import expressWinston from 'express-winston';
import winstonMongodb from 'winston-mongodb';

const MongoDB = winstonMongodb.MongoDB;

expressWinston.requestWhitelist.push('body');
app.use(expressWinston.logger({
    transports: [
        // 控制臺
        new winston.transports.Console({
            json: true,
            colorize: true
        }),
        // 文件
        new winston.transports.File({
          filename: './logs/success.log'
        }),
        // mongodb數(shù)據(jù)庫
        new winston.transports.MongoDB({ 
            db: config.logsUrl
        })
    ],
    meta: true,
    msg: "HTTP {{req.method}} {{req.url}}",
    expressFormat: true,
    colorize: true,
    ignoreRoute: function (req, res) { return false; }
}));

其中db的寫法參考如下,常見問題

db : 'mongodb://localhost:27017/Book-catalog',

有的開發(fā)者可能不太了解,會選擇將一些日志寫入到數(shù)據(jù)庫中。數(shù)據(jù)庫比日志好的地方在于它是結(jié)構(gòu)化的數(shù)據(jù),可以直接使用SQL語言進(jìn)行查詢和統(tǒng)計,日志文件則需要在加工之后才能分析。
但是日志文件和數(shù)據(jù)庫在寫入性能上是兩個級別,數(shù)據(jù)庫在寫入過程中要經(jīng)歷一系列處理,比如鎖表、日志等操作。寫日志文件則是直接將數(shù)據(jù)寫到磁盤上。相比之下,寫日志是輕量型操作,將日志分析和日志記錄步驟分離開來是較好的選擇。

線上業(yè)務(wù)可能訪問量巨大,產(chǎn)生的日志也可能是大量的,上述示例只是簡單的將普通日志和異常日志分開存放至兩個文件中,日志過多時也不便產(chǎn)看。為此,將產(chǎn)生的日志按日期分割是必要的。expressWinston可以使用winston-daily-rotate-file,引入第三個庫,目前看有點(diǎn)略麻煩,同時還不能將自己的輸出打入到日志中,我們準(zhǔn)備介紹下一個日志工具log4js。

2.log4js

Node.js,已經(jīng)有現(xiàn)成的開源日志模塊,就是log4js,源碼地址:點(diǎn)擊打開鏈接
項目引用方法:

npm install log4js

用法也是非常見簡單:

var log4js = require('log4js');
var logger = log4js.getLogger();
logger.level = 'debug'; // default level is OFF - which means no logs at all.
logger.debug("Some debug messages");

配置

log4js.configure(object || string)

配置屬性包含有:

  • levels:用來定義日志等級
  • appenders:map結(jié)構(gòu),定義日志輸入,必需定義type字段
  • categories:map結(jié)構(gòu),定義分類,必需定義default分類,分離里面包含appenders和level屬性
  • pm2:pm2日志

實例1

const log4js = require('log4js');
log4js.configure({
  appenders: {
    out: { type: 'stdout' },
    app: { type: 'file', filename: 'application.log' }
  },
  categories: {
    default: { appenders: [ 'out', 'app' ], level: 'debug' }
  }
});

代碼中定義了兩個appenders,分別為out和app。out使用stdout追加日志方法,獨(dú)立輸出。app使用文件作為輸入,將內(nèi)容輸出到application.log

Loggers

log4js.getLogger([category])

輸入哪種分類的log

Shutdown

log4js.shutdown(cb)

shutdown方法用于關(guān)閉log的接收,結(jié)束所有寫日志事件。

Custom Layouts

log4js.addLayout(type, fn)

用戶自定義數(shù)據(jù)記錄格式。

實例2

log4js.configure({
  appenders: { 'out': { type: 'stdout', layout: { type: 'basic' } } },
  categories: { default: { appenders: ['out'], level: 'info' } }
});
const logger = log4js.getLogger('cheese');
logger.error('Cheese is too ripe!');

該配置替換stdout的默認(rèn)格式coloured,替換為basic。輸入為:

[2017-03-30 07:57:00.113] [ERROR] cheese - Cheese is too ripe!

實例3

log4js.configure({
  appenders: { out: { type: 'stdout', layout: { type: 'messagePassThrough' } } },
  categories: { default: { appenders: [ 'out' ], level: 'info' } }
});
const logger = log4js.getLogger('cheese');
const cheeseName = 'gouda';
logger.error('Cheese is too ripe! Cheese was: ', cheeseName);

該配置替換stdout的默認(rèn)格式coloured,替換為messagePassThrough。輸入為:

Cheese is too ripe! Cheese was: gouda

實例4
不得不說的type: 'pattern',通過正則輸出相應(yīng)的格式化數(shù)據(jù):

log4js.configure({
  appenders: {
    out: { type: 'stdout', layout: {
      type: 'pattern',
      pattern: '%d %p %c %X{user} %m%n'
    }}
  },
  categories: { default: { appenders: ['out'], level: 'info' } }
});
const logger = log4js.getLogger();
logger.addContext('user', 'charlie');
logger.info('doing something.');

通過正則匹配也可以定義傳遞變量,如上代碼的user。輸出為:

2017-06-01 08:32:56.283 INFO default charlie doing something.

實例5
定義自己的輸出格式,type: 'json',如下所示:

const log4js = require('log4js');

log4js.addLayout('json', function(config) {
  return function(logEvent) { return JSON.stringify(logEvent) + config.separator; }
});

log4js.configure({
  appenders: {
    out: { type: 'stdout', layout: { type: 'json', separator: ',' } }
  },
  categories: {
    default: { appenders: ['out'], level: 'info' }
  }
});

const logger = log4js.getLogger('json-test');
logger.info('this is just a test');
logger.error('of a custom appender');
logger.warn('that outputs json');
log4js.shutdown(() => {});

輸出為:

{"startTime":"2017-06-05T22:23:08.479Z","categoryName":"json-test","data":["this is just a test"],"level":{"level":20000,"levelStr":"INFO"},"context":{}},
{"startTime":"2017-06-05T22:23:08.483Z","categoryName":"json-test","data":["of a custom appender"],"level":{"level":40000,"levelStr":"ERROR"},"context":{}},
{"startTime":"2017-06-05T22:23:08.483Z","categoryName":"json-test","data":["that outputs json"],"level":{"level":30000,"levelStr":"WARN"},"context":{}},

具體使用

新建log.js文件,內(nèi)容如下所示:

var log4js = require('log4js');
log4js.configure({
    appenders: {
        out: { type: 'console' }, //控制臺輸出  
        app: {
            type: "dateFile",
            filename: 'logs/log.log',
            pattern: "_yyyy-MM-dd",
            alwaysIncludePattern: false,
        }//日期文件格式  
    },
    categories: {
        default: { appenders: ['out'], level: 'info' },
        logFile: { appenders: ['out', 'app'], level: 'ALL' },
    },
    replaceConsole: true,   //替換console.log  
});

var fileLog = log4js.getLogger('logFile');
exports.logger = fileLog;
exports.use = function (app) {
    //頁面請求日志
    app.use(log4js.connectLogger(fileLog));
}

輸入的log如下所示:

[2017-09-18 20:37:02.366] [INFO] logFile - ::1 - - "POST /user/login HTTP/1.1" 200 393 "" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容