關(guān)于自定義Egg.js的請(qǐng)求級(jí)別日志詳解
Egg.js 是什么?
Egg.js 為企業(yè)級(jí)框架和應(yīng)用而生,我們希望由 Egg.js 孕育出更多上層框架,幫助開(kāi)發(fā)團(tuán)隊(duì)和開(kāi)發(fā)人員降低開(kāi)發(fā)和維護(hù)成本。
注:Egg.js 縮寫為 Egg
背景
組織為了更好的對(duì)各個(gè)業(yè)務(wù)的請(qǐng)求日志進(jìn)行統(tǒng)一的分析,制定了統(tǒng)一的日志打印規(guī)范,比如:
[time][processId][traceId][userid] Hello World....
統(tǒng)一格式之后,業(yè)務(wù)現(xiàn)有業(yè)務(wù)的日志工具打印出來(lái)的格式是無(wú)法滿足該規(guī)范的,所以我們需要對(duì)此進(jìn)行改造。
我們前端目前Node中間層使用的框架是Egg.js,所以下文講述下如何在Egg.js上自定義請(qǐng)求日志格式。
開(kāi)始動(dòng)手
Egg.js中自帶了三種logger,分別是
- Context Logger
- App Logger
- Agent Logger
Context Logger主要是用來(lái)記錄請(qǐng)求相關(guān)的日志。每行日志都會(huì)在開(kāi)頭自動(dòng)的記錄當(dāng)前請(qǐng)求的一些信息,比如時(shí)間、ip、請(qǐng)求url等等。
App Logger用于記錄應(yīng)用級(jí)別的日志,比如程序啟動(dòng)日志。
Agent Logger用于記錄多進(jìn)程模式運(yùn)行下的日志。
我們想自定義請(qǐng)求級(jí)別的日志,那重點(diǎn)就要從Context Logger去研究怎么做。最理想的方案就是,Context Logger本身支持配置化的自定義格式,通過(guò)在egg.js的config配置文件中,通過(guò)傳入formatter的參數(shù)就能自定義。
//config.default.js exports.customLogger = { log: { file: 'appname.log', formatter: (message)=>{ return `${message.time}${message.processid}` } } }
但不久我們發(fā)現(xiàn)這條路走不通,設(shè)置了這個(gè)formatter并不起作用。從Context Logger的源碼中,我們發(fā)現(xiàn)的端倪context_logger.js
[ 'error', 'warn', 'info', 'debug' ].forEach(level => { const LEVEL = level.toUpperCase(); ContextLogger.prototype[level] = function() { const meta = { formatter: contextFormatter, paddingMessage: this.paddingMessage, }; this._logger.log(LEVEL, arguments, meta); }; }); module.exports = ContextLogger; function contextFormatter(meta) { return meta.date + ' ' + meta.level + ' ' + meta.pid + ' ' + meta.paddingMessage + ' ' + meta.message; }
在源碼中我們可以看到,formatter參數(shù)已經(jīng)被內(nèi)部的一個(gè)自定義格式化函數(shù)覆蓋了,配置中寫的是不會(huì)啟作用的。
此路不通,只能嘗試自己實(shí)現(xiàn)logger去解決。自己實(shí)現(xiàn)我們需要考慮一些點(diǎn),比如:
- 日志要寫到文件中,錯(cuò)誤日志單獨(dú)寫一個(gè)文件
- 需要能按天或按小時(shí)切割日志
- IO性能
如果這些都自己實(shí)現(xiàn)的話,那就太麻煩了。好在了解到Egg的這幾個(gè)logger都是基于egg-logger和egg-logrotator去實(shí)現(xiàn)的,所以我們可以站在巨人的肩膀上搞事情。
Context Logger是基于egg-logger的FileTransport類去進(jìn)行文件落地的,同時(shí)FileTransport也默認(rèn)配置了egg-logrotator的日志拆分。所以,我們只需要繼承FileTransport類,實(shí)現(xiàn)接口就可以了,代碼如下:
//CoustomTransport.js const FileTransport = require('egg-logger').FileTransport; const moment = require('moment'); class CoustomTransport extends FileTransport { constructor(options, ctx) { super(options); this.ctx = ctx; } log(level, args, meta) { const prefixStr = this.buildFormat(level); for (let i in args) { if (args.hasOwnProperty(i)) { if (parseInt(i, 10) === 0) { args[i] = `${prefixStr}${args[i]}`; } if (parseInt(i, 10) === args.length - 1) { args[i] += '\n'; } } } super.log(level, args, meta); } buildFormat(level) { const timeStr = `[${moment().format('YYYY-MM-DD HH:mm:ss.SSS')}]`; const threadNameStr = `[${process.pid}]`; const urlStr = `[${this.ctx.request.url}]` return `${timeStr}${threadNameStr}${urlStr}`; } setUserId(userId) { this.userId = userId; } } module.exports = CoustomTransport;
我們通過(guò) logger.info('Hello World')
去打印日志,格式則顯示為我們自定義的格式。
到這,自定義日志格式解決了,那我們?nèi)绾潍@取每次請(qǐng)求的信息呢?這里就要借助Egg.js框架對(duì)Context的擴(kuò)展功能, Context是請(qǐng)求級(jí)別的對(duì)象,我們?cè)?a rel="external nofollow" target="_blank" >Context的原型上擴(kuò)展方法可以拿到該對(duì)象帶有的每次請(qǐng)求的信息。
//CustomLogger.js const Logger = require('egg-logger').Logger; const CoustomTransport = require('./CoustomTransport.js'); module.exports = function(ctx){ const logger = new Logger(); logger.set('file', new CoustomTransport({ level: 'INFO', file: 'app.log' }, ctx)); return logger; }; // app/extend/context.js /* * Context對(duì)象擴(kuò)展 * */ const Logger = require('egg-logger').Logger; const CoustomTransport = require('./CoustomTransport'); const CustomLogger = require('./CustomLogger'); module.exports = { get swLog() { return CustomLogger(this); } };
調(diào)用
// app/controller/home.js module.exports = app => { class HomeController extends app.Controller { async index() { this.ctx.swLog.info('Hello World'); } } return HomeController; };
結(jié)果
[2018-11-02 19:25:09.665][22896][/] Hello World
到此,我們就能完整的自定義請(qǐng)求級(jí)別的日志了。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
全面解析JavaScript中“&&”和“||”操作符(總結(jié)篇)
這篇文章主要介紹了全面解析JavaScript中“&&”和“||”操作符(總結(jié)篇)的相關(guān)資料,需要的朋友可以參考下2016-07-07JavaScript操作URL的相關(guān)內(nèi)容集錦
這篇文章主要介紹了JavaScript操作URL的相關(guān)內(nèi)容集錦的相關(guān)資料,需要的朋友可以參考下2015-10-10JavaScript中Array實(shí)例方法filter的實(shí)現(xiàn)原理
filter() 方法創(chuàng)建一個(gè)新數(shù)組,其中包含通過(guò)所提供函數(shù)實(shí)現(xiàn)的測(cè)試的所有元素,本文將給大家介紹JavaScript中Array實(shí)例方法filter的實(shí)現(xiàn)原理,文中通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-03-03layer 刷新某個(gè)頁(yè)面的實(shí)現(xiàn)方法
今天小編就為大家分享一篇layer 刷新某個(gè)頁(yè)面的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09js實(shí)現(xiàn)Select頭像選擇實(shí)時(shí)預(yù)覽代碼
這篇文章主要介紹了js實(shí)現(xiàn)Select頭像選擇實(shí)時(shí)預(yù)覽代碼,涉及javascript動(dòng)態(tài)遍歷及設(shè)置select選項(xiàng)的技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-08-08JavaScript實(shí)現(xiàn)鼠標(biāo)移動(dòng)事件畫筆
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)鼠標(biāo)移動(dòng)事件畫筆,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08webpack 自動(dòng)清理 dist 文件夾的兩種實(shí)現(xiàn)方法
這篇文章主要介紹了webpack 自動(dòng)清理 dist 文件夾的兩種實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06