vlambda博客
学习文章列表

log4js日志管理(基于Nodejs)

log4js日志管理

0 码云仓库代码(欢迎Start)

码云仓库代码-https://gitee.com/hongjunyong/markdown-collect/tree/master

1 基本介绍

1.1 源码及文档

https://github.com/log4js-node/log4js-node

1.2 介绍

  • 显示程序运行状态
  • 帮助开发者排除问题故障
  • 结合专业的日志分析工具(如 ELK )给出预警

1.3 log4j的8个日志级别

log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。

  1. ALL 最低等级的,用于打开所有日志记录。
  2. TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,一般不会使用。
  3. DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
  4. INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
  5. WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
  6. ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
  7. FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
  8. OFF 最高等级的,用于关闭所有日志记录。

如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。例如,如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出,而INFO、DEBUG、TRACE、 ALL级别的log则会被忽略。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。

1.4 安装

 npm install log4js --save-dev // log4js
 npm install ip --save-dev  // 获取ip地址

1.5 文件解析

1 目录结构

|- middleware
|- log4js
|- access.js 记录和配置客户端信息
|- index.js 对日志中间件进行错误处理
|- logger.js log4js记录日志
目录结构

2 记录说明

记录说明

2 代码解析

2.1 在koa入口文件(app.js)注册日志中间件

 // 引入日志中间件
 const logger = require("../middleware/log4js/index");
 const ip = require("ip");
 // 注册中间件
 app.use(logger({
   // koa提供的环境变量
   env: app.env,
   // 项目名,记录在日志中的项目信息
   projectName: 'vue-vant',
   // 指定记录的日志级别
   applogLevel: 'debug',
   // 指定日志存放的目录名
   dir: 'logs',
   // 默认情况下服务器 ip 地址
   serverIp: ip.address()
 }));

2.2 index.js入口文件 --> 对日志中间件进行错误处理

const logger = require('./logger');
module.exports = (options) => {
    const loggerMiddleware = logger(options);

    return (ctx, next) => {
        return loggerMiddleware(ctx, next)
            .catch((e) => {

                // 如果中间件里面有抛出错误,这里将通过 catch 函数捕捉到并处理,将状态码小于 500 的错误统一按照 500 错误码处理,以方便后面的 http-error 中间件显示错误页面。
                if (ctx.status < 500) {
                    ctx.status = 500;
                }
                // 异常日志打印
                ctx.log.error(e.stack);
                ctx.state.logged = true;
                ctx.throw(e);
            })
    }
}

2.3 logger.js记录日志

2.3.1 参数定义

 // 将日志的不同级别提取为数组,方便后面做处理
 const methods = ['trace''debug''info''warn''error''fatal''mark'];

    let contextLogger = {};
 // 提取默认公用参数对象(以防注册中间件,用户没有传入参数)
    const defaultInfo = {
        // 开发环境
        env: 'dev',
        // 输出的文件夹名称
        dir: 'logs',
        // 错误级别
        applogLevel: 'info'
    };
    // 继承自 defaultInfo 默认参数(options会把defaultInfo里的参数覆盖掉)
    const opts = Object.assign({}, defaultInfo, options || {});
    // 需要的变量解构 方便使用
    const { env, dir, applogLevel } = opts;

2.3.2 日志文件命名、日志切割

    const appenders = { cheese: {
     // 日志类型
     type'dateFile',
     // 输出日志文件名称
     filename: `server/${dir}/task`,
     // 名称后面添加年月日
     pattern: '-yyyy-MM-dd.log',
     // 是否总是有后缀名
     alwaysIncludePattern: true
    } };

2.3.3 把日志在终端输出(默认关闭,需要时可以把注释去掉)

    // if (env === 'dev' || env === 'local' || env === 'development') {
    //     appenders.out = {
    //         type'console'
    //     }
    // }

2.3.4 指定日志的默认配置项

    const config = {
        appenders: appenders,
        /**
         * 指定日志的默认配置项
         * 如果 log4js.getLogger 中没有指定,默认为 cheese 日志的配置项
         * 指定 cheese 日志的记录内容为 error 及 error 以上级别的信息
         *
         * var obj = {'a':'123','b':'345'};
         * console.log(Object.keys(obj));  //['a','b']
         *
         * Object.keys(appenders) // cheese
         */
        categories: {
            default: {
                appenders: Object.keys(appenders),
                level: applogLevel
            }
        }
    };

2.3.5 计算响应时间

1 记录请求开始的时间

    const start = Date.now();
    log4js.configure(config);

    await next();

2 记录请求结束的时间

  const end = Date.now();
  const responseTime = end - start;

3 响应时间

响应时间为${responseTime/1000}s

2.3.5 循环methods将所有方法挂载到ctx上

    methods.forEach((method, i) => {
        contextLogger[method] = (message) => {
            // 将入参换为函数返回的字符串
            logger[method](access(ctx, message, {}))
        }
    });
    ctx.log = contextLogger;
  • 调用:ctx.log.error('错误啦')

2.3.6 把日志输出在指定的文件里,并且把options操作过滤

 // 过滤掉options操作(跨域)
    if (ctx.request.method != 'OPTIONS') {
        logger.info(access(ctx, `响应时间为${responseTime/1000}s`, {}));
    }

2.4 access.js封装打印信息

2.4.1 获取用户提交数据、服务端返回的json

    // 请求方法
    var getMethod = ctx.request.method;
    // 用户提交的数据
    var params = '';
    // 服务端返回的json
    var data = '';

    if (getMethod === 'GET') {
        params = ctx.query
    } else if (getMethod === 'POST') {
        params = ctx.request.body
    } else if (getMethod === 'PUT') {
        params = ctx.request.body
    } else if (getMethod === 'DELETE') {
        params = ctx.query
    }
    data = ctx.body

2.4.2 获取参数

    const {
        // 请求方法 get post或其他
        method,
        // 请求链接
        url,
        // 发送请求的客户端的host
        host,
        // 请求中的headers
        headers
    } = ctx.request;

2.4.3 返回打印信息

 const client = {
        method,
        url,
        host,
        message,
        params,
        data,
        // 请求的源地址
        referer: headers['referer'],
        // 客户端信息设备 及 浏览器信息
        userAgent: headers['user-agent'],
    };
    // client的参数拷贝给commonInfo
    return JSON.stringify(Object.assign(commonInfo, client));

2.5 THE END