一個node系統的日誌管理

已經好久沒有學習了,趁需求不飽和,想經過學習組裏的成熟的node系統,模仿搭建一個「健全」的node系統。前端

通常第一步確定是怎麼配置基礎信息,讓這個系統跑起來。但是。。。恰好我不會日誌管理,想研究下怎麼打logger,因此。。第一篇我就來寫,這個系統的日誌管理怎麼作。我用的node框架是express,日誌管理工具是log4js。java

何時什麼信息須要打日誌?

我總結了下幾個可能須要打日誌的信息,可根據實際狀況增刪
一、前端的請求。(爲了方便排查問題)
二、返回的響應。
三、系統間的交互行爲。一個成熟的項目,應該不會只有一個node系統。我以爲node更多充當一箇中間層,若是node能處理的問題能夠node處理不麻煩java童鞋,但一些不適合node處理好比CPU密集型或邏輯複雜的確定交給java童鞋處理會比較好。雖然多了一次http請求,可是這些時間實際上是能夠忽略不計的。因此在node調用各個系統接口什時,我以爲仍是有必要打印body和response。除了利於排查問題外,還能關鍵時候不背鍋。
三、其餘的手動logger。node

怎麼用?

一、一個req請求貫穿整個請求了。前端的請求和返回的響應都跟req請求相關,若是把這個log4js掛載在req上,那麼咱們使用起來就很方便了。express的中間件能夠幫咱們實現這個功能。使用方式:req.logger.info(msg)
二、至於node系統與其餘java系統交互。一個成熟的系統,確定會把這個請求交互封裝成一個class,因此只要咱們在這個class的request方法打logger,就能夠實現一個地方寫,每一個請求都自動打logger,對使用者來講無感且方便。express

logger的分類

log4js支持ALLTRACEDEBUGINFOWARNERRORFATALOFF8種,但通常使用info和error兩種。
雖然同一個文件能夠存不一樣類型的日誌,但把info類型和error類型分開兩個文件存,有幾個好處:
一、能夠對error的日誌類型進行監控,及時報警
二、存儲的時間能夠有所不一樣,info類型存近15天,error類型存近1個月。
三、若是node系統與多個系統都有交互,好比A系統是跟帳號相關的功能、B系統與文章相關的功能、C系統與商品相關的功能等等,這時候也能夠根據系統對日誌進行分類:A系統的日誌在一個文件夾,B系統的日誌一個文件夾。並且根據日誌量,分不一樣的年月日時分來存。
把日誌分類,都是爲了利於排查問題!
日誌文件是如下的結構:app

systemA
 --error
   --2018-09-26.log
 --info
   --2018-09-26.log
systemB
 --error
   --2018-09-26.log
 --info
   --2018-09-26-17.log
   --2018-09-26-18.log

實現

說了這麼多,立刻來實現了
先把log4js配置好框架

//simpleType.js  這個js定義了有哪幾個系統類型(就是有哪些文件夾)
module.exports = [
    'systemA',
    'systemB',
    'systemC',
    'systemD'
]
//logger.js  這個js初始化了配置並初始化
const path = require('path')
const fs = require('fs')
const log4js = require('log4js')
const category = require('./simpleTypes')

let logger_conf = {
    appenders: {
        console: {
            type: 'console'
        }
    },
    replaceConsole: true,//控制檯日誌
    categories: {
        default: {
            appenders: ['console'],
            level: 'info'
        }
    }
}

const DEFAULT_PATTERN = 'yyyy-MM-dd-hh.log'

if(process.env.UAE_MODE) {  //生產環境纔有日誌文件
    category.forEach(c => {
        let dirPath = path.join(__dirname, `../logs/${c}`)
        if(!fs.existsSync(dirPath)) fs.mkdirSync(dirPath);
        let infoPath = path.join(dirPath, 'info/');
        let errorPath = path.join(dirPath, 'error/');
        ['Info', 'Error'].forEach(type => {  //其實這裏的配置我以爲有點毛病,請大神指出
            logger_conf.appenders[`${c}${type}`] = {
                type: 'dateFile',
                pattern: DEFAULT_PATTERN,
                filename: infoPath,
                alwaysIncludePattern: true,
                category: `${c}${type}`
            }
            logger_conf.categories[`${c}${type}`] = {
                appenders: [`${c}${type}`, 'console'],
                level: type.toLowerCase()
            }
        })
    })
}
if(logger_conf.appenders) {
    for(var key in logger_conf.appenders) {
        if(logger_conf.appenders[key].filename)
            checkFile(logger_conf.appenders[key].filename) //必定存在文件夾,否則會出錯
    }
}
log4js.configure(logger_conf)

function checkFile(dir) {
    if(!fs.existsSync(dir)){
        fs.mkdirSync(dir);
    }
}
module.exports = log4js;  //對外暴露一個log4js實例

配置好,那就是使用了
首先是req的掛載。使用方式:req.systemAInfo.info('req logger msg')工具

//middleware/logger.js  middleware文件夾專門存放中間件,後續文章會講
const log4js = require('../logger/logger')
const simpleTypes = require('../logger/simpleTypes')

module.exports = function(req) {
    simpleTypes.forEach(system => {
        ['Info', 'Error'].forEach(type => {
            var log = `${system}${type}`
            req[log] = log4js.getLogger(log)
        })
    })
}

//index.js
app.use('*', function(req, res, next) {
    reqLogger(req)   //經過express的中間件對req掛載
    next()
})

至於系統層級的,每一個系統的class都繼承一個Base class,在Base class裏實現學習

// Base.js
const request = require('request')
const log4js = require('../logger/logger')

module.exports = class Base {
    constructor(id) {
        this.id = id;
    }

    request(opts, cb) {
        let infoLogger = log4js.getLogger(`${this.id}Info`)
        let errorLogger = log4js.getLogger(`${this.id}Error`)

        opts = this._requestFilter(opts)//  各個系統鑑權

        let body = JSON.stringify(opts)
        infoLogger.info(body)
        request(opts, (err, res, body) => {
            if(err) errorLogger.error(JSON.stringify(err))
            else if(body && body.error) errorLogger.error(JSON.stringify(body.error))
            else{
                infoLogger.info(JSON.stringify(body))
                cb(err, body)
            }    
        })
    }

    _requestFilter(opts) {
        return JSON.parse(JSON.stringify(opts))
    }
}

//systemA.js
const Base = require('./Base')
class systemA extends Base {
    constructor() {
        super('systemA')
    }

    _requestFilter() {
        //systemA的鑑權,後面文章   
    }
}
mudole.exports = systemA

好了,一個日誌管理就初成型了ui

相關文章
相關標籤/搜索