浅谈nodejs加log4js日志管理

日前有一个项目,是小程序项目,需要加入日志进行监控。小程序采用koa+qcloud2架构,准备使用log4js来做日志管理。基于项目的架构,就引入了koa-log4

首先安装koa-log4

npm install koa-log4 —save

log4js v2.x的配置:

{
  "appenders": {
"access": {
  "type": "dateFile",
  "filename": "logs/access.log",
  "pattern": "-yyyy-MM-dd"
},
"rule-console": {
  "type": "console"
},
"rule-file": {
  "type": "dateFile",
  "filename": "logs/server-",
  "encoding": "utf-8",
  "maxLogSize": 10000000,
  "numBackups": 3,
  "pattern": "yyyy-MM-dd.log",
  "alwaysIncludePattern": true
},
"rule-error": {
  "type": "dateFile",
  "filename": "logs/error-",
  "encoding": "utf-8",
  "maxLogSize": 1000000,
  "numBackups": 3,
  "pattern": "yyyy-MM-dd.log",
  "alwaysIncludePattern": true,
  "level": "error"
}
  },
  "categories": {
"default": {
  "appenders": [
"rule-console"
  ],
  "level": "all"
},
"resLogger": {
  "appenders": [
"rule-file"
  ],
  "level": "info"
},
"errorLogger": {
  "appenders": [
"rule-error"
  ],
  "level": "error"
},
"http": {
  "appenders": [
"access"
  ],
  "level": "info"
}
  }
}

ps:

appenders配置的是具体的策略,分别有console、file。而categories主要是针对多种情况所做的配置,可以对不同的场景配置不同的策略,比如error日志对应rule-error的策略单独放到error的文件,info日志放到普通文件,这样就可以区分错误和普通日志。

获取配置的代码  log4js.getLogger("resLogger")

log4js的使用:

一般来说我们要求统一加日志,要把接口的输入和输出都记录下来,而不用手动去为每个接口去加日志,那样太麻烦了。并且要记录错误日志,在发生问题的时候可以清楚我们的系统究竟出了什么问题,快速定位问题并解决问题。

so,我们基本锁定中间件的方案,加一个中间件,在接口的响应部分包上一层,并且统一捕捉异常。统一处理接口日志和错误日志。也方便将错误替换成其他提示返回给前端。

const debug = require('debug')('koa-weapp-demo')
const logUtil = require('../tools/logUtil.js')

/**
响应处理模块
 */
module.exports = async function (ctx, next) {
  //响应开始时间
  const start = new Date();
  //响应间隔时间
  var ms;
  try {
// 调用下一个 middleware
await next()

ms = new Date() - start;
//记录响应日志
_logUtil.logResponse(ctx, ms);_

// 处理响应结果
// 如果直接写入在 body 中,则不作处理
// 如果写在 ctx.body 为空,则使用 state 作为响应
ctx.body = ctx.body ? ctx.body : {
  code: ctx.state.code !== undefined ? ctx.state.code : 0,
  data: ctx.state.data !== undefined ? ctx.state.data : {}
}
  } catch (e) {
// catch 住全局的错误信息
debug('Catch Error: %o', e)

ms = new Date() - start;
//记录异常日志
_logUtil.logError(ctx, e, ms);_


// 设置状态码为 200 - 服务端错误
ctx.status = 200

// 输出详细的错误信息
ctx.body = {
  code: -1,
  error: e && e.message ? e.message : e.toString()
}
  }
}


const response = require('./middlewares/response')
// 使用响应处理中间件
app.use(response)

而log4js需要自己封装成一个工具类,除了要具备能将响应记录在文件里外,还要有能将普通日志和错误信息写进文件的能力

logUtil我引用这里,感觉写得很好,对我很有帮助,在这个基础上我做了修改,小程序在腾讯云上没有更多的写入权限(/root/logs),所以引用log4js会导致测试环境跑不了,切记https://www.cnblogs.com/smartsensor/p/7838169.html

/**
使用方法:
logUtil.resLogger.info('test')
logUtil.errorLogger.error('test')
logUtil.logInfo('test')
const start = new Date()
var ms
logUtil.logResponse(ctx, ms)
logUtil.logError(ctx, error, ms)
 */

var config = require('../config.js')
if (!config.useQcloudLogin) {
  var log4js = require('log4js');

  var log_config = require('../log4js.json');

  //加载配置文件
  log4js.configure(log_config);

  var logUtil = {};
  //调用预先定义的日志名称
  var resLogger = log4js.getLogger("resLogger");
  var errorLogger = log4js.getLogger("errorLogger");
  var consoleLogger = log4js.getLogger();

  console.log = resLogger.info.bind(resLogger)

  logUtil.resLogger = resLogger
  logUtil.errorLogger = errorLogger
  logUtil.consoleLogger = consoleLogger


  //封装错误日志
  logUtil.logError = function (ctx, error, resTime) {
if (ctx && error) {
  errorLogger.error(formatError(ctx, error, resTime));
}
  };

  //封装响应日志
  logUtil.logResponse = function (ctx, resTime) {
if (ctx) {
  resLogger.info(formatRes(ctx, resTime));
}
  };

  logUtil.logInfo = function (info) {
if (info) {

  consoleLogger.info(formatInfo(info));
}
  };

  var formatInfo = function (info) {
var logText = new String();
//响应日志开始
logText += "\n" + "***************info log start ***************" + "\n";

//响应内容
logText += "info detail: " + "\n" + JSON.stringify(info) + "\n";

//响应日志结束
logText += "*************** info log end ***************" + "\n";

return logText;
  }

  //格式化响应日志
  var formatRes = function (ctx, resTime) {
var logText = new String()
//响应日志开始
logText += "\n" + "*************** response log start ***************" + "\n"

//添加请求日志
logText += formatReqLog(ctx.request, resTime)

//响应状态码
logText += "response status: " + ctx.status + "\n"

//响应内容
logText += "response body: " + "\n" + JSON.stringify(ctx.body) + "\n"

//响应日志结束
logText += "*************** response log end ***************" + "\n"

return logText

  }

  //格式化错误日志
  var formatError = function (ctx, err, resTime) {
var logText = new String()

//错误信息开始
logText += "\n" + "*************** error log start ***************" + "\n"

//添加请求日志
logText += formatReqLog(ctx.request, resTime)

//错误名称
logText += "err name: " + err.name + "\n"
//错误信息
logText += "err message: " + err.message + "\n"
//错误详情
logText += "err stack: " + err.stack + "\n"

//错误信息结束
logText += "*************** error log end ***************" + "\n"

return logText
  };

  //格式化请求日志
  var formatReqLog = function (req, resTime) {

var logText = new String()

var method = req.method
//访问方法
logText += "request method: " + method + "\n"

//请求原始地址
logText += "request originalUrl:  " + req.originalUrl + "\n"

//客户端ip
logText += "request client ip:  " + req.ip + "\n"

logText += "request header:  " + JSON.stringify(req.header) + "\n"

//开始时间
var startTime
//请求参数
if (method === 'GET') {
  logText += "request query:  " + JSON.stringify(req.query) + "\n"
} else {
  logText += "request body: " + "\n" + JSON.stringify(req.body) + "\n"
}
//服务器响应时间
logText += "response time: " + resTime + "\n"

return logText
  }

  module.exports = logUtil
} else {
  var logUtil = {};
  logUtil.resLogger = {};
  logUtil.resLogger.info = function (msg) {
  }
  logUtil.resLogger.error = function (msg) {
  }

  logUtil.errorLogger = {};
  logUtil.errorLogger.info = function (msg) {
  }
  logUtil.errorLogger.error = function (msg) {
  }

  logUtil.consoleLogger = {};
  logUtil.consoleLogger.info = function (msg) {
  }
  logUtil.consoleLogger.error = function (msg) {
  }

  logUtil.logError = function (ctx, error, resTime) {
  }
  logUtil.logResponse = function (ctx, resTime) {
  }
  logUtil.logInfo = function (info) {
  }
  module.exports = logUtil
}

可以将console.log重定向到log4js统一管理,如下:

console.log = resLogger.info.bind(resLogger)

使用koa-log4中间件直接记录log,我没有研究到什么有用的东西,记录的日志也不是有用的。如下

const log4js = require('koa-log4')
app.use(log4js.koaLogger(log4js.getLogger("http"), { level: 'auto' }))

到这里就可以看log4js的威力,基本上可以满足我小程序的日志需求,使用起来挺方便的。