已经好久没有学习了,趁需求不饱和,想经过学习组里的成熟的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
log4js支持ALL
、TRACE
、DEBUG
、INFO
、WARN
、ERROR
、FATAL
、OFF
8种,但通常使用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