zoukankan      html  css  js  c++  java
  • Log4js 工作原理及代码简析

    本文地址 http://www.cnblogs.com/jasonxuli/p/6518650.html
     
    log4js
     
    版本 0.6.16, 最新版1.1.1 大体类似。
     
    使用 log4js 时,基本的流程是:
    1,声明 config 配置;
    2,log4js.configure(config);
    3, log4js.getLogger(categoryName);
     
    配置
     
    主要有下面几个要点:
     
    type: 表明 appender 的类型,对应 log4js/lib/appenders/ 目录下的文件名, log4js 会在加载配置文件时根据 type 加载对应文件。
         categoryFilter.js, clustered.js, console.js, dateFile.js, file.js, fileSync.js, gelf.js, hookid.js, loggly.js, logLevelFilter.js, multiprocess.js, smtp.js
              
    category:表明 appender 的分类,用户自定义;如果不指定,加载时默认值为 [all];相同 category 的 appender 会被添加到内部变量 appenders {category : appender} 中。这个 appenders 的作用体现在之后通过 getLogger() 获取 logger 时;
     
    level: log4js 的 level 如下
    module.exports = {
      ALL: new Level(Number.MIN_VALUE, "ALL"),
      TRACE: new Level(5000, "TRACE"),
      DEBUG: new Level(10000, "DEBUG"),
      INFO: new Level(20000, "INFO"),
      WARN: new Level(30000, "WARN"),
      ERROR: new Level(40000, "ERROR"),
      FATAL: new Level(50000, "FATAL"),
      OFF: new Level(Number.MAX_VALUE, "OFF"),
      toLevel: toLevel
    };
    appender 结构:参看下面的示例,简单类型例如 file,fileSync等,只需要简单的对象,不需要内部再嵌套一个 appender; 对于复杂类型例如 logLevelFilter,categoryFilter等,最终还是需要一个简单类型去写日志,因此需要嵌套一个 appender 。
     
    var config = {
        appenders    : [
            {
                type: "console"
            },
            {
                type    : "file",
                filename: "/var/log/kernel/test.log"
            },
            {
                type    : "logLevelFilter",
                level  : "ALL",
                appender: {
                    type    : "file",
                    filename: "/var/log/kernel/kernel.log",
                    layout:{
                        type:"pattern",
                        pattern: "[%h %x{pid}] - [%d] [%p] %c %m",
                        tokens: {
                            pid: function(){return process.pid}
                        }
                    }
                }
            },
            {
                type    : "logLevelFilter",
                level  : "ERROR",
                appender: {
                    type    : "file",
                    filename: "/var/log/kernel/kernelerr.log"
                }
            },
            {
                type    : "file",
                filename: "/var/log/kernel/cron.log",
                category: "cron"
            },
            {
                type    : "file",
                filename: "/var/log/kernel/mem.log",
                category: "memory"
            }
        ],
        replaceConsole: true
    };
     
    log4js.configure(config);
     配置加载流程
     
    根据log4js.js 中的代码次序,关键的函数是下面几个 : 
     
    1,configure() : 
          入口函数
     
    2,loadAppender(appender, appenderModule) :
         根据 type 加载 log4js/lib/appenders/ 目录下对应的 appender 模块;之后调用该模块的 configure() 加载该 appender 配置,返回最终负责写 log 的函数 function(loggingEvent); 因此 logLevelFilter 这样的"高级"模块就会寻找 config.appender 属性进行后续配置;
     
    3,addAppenderToCategory(appender, category)
         将 category 和加载后的 appender 作为键值对添加到 appenders 对象;
     
    4,addAppenderToAllLoggers(appender)
         将没有指定 category 的 appender 默认为 [all],添加到 logger 缓存对象 loggers 中; 
     
    其实不太明白这里为什么要先添加 [all] 到 loggers 缓存中,毕竟 getLogger() 函数中 categoryName 的默认值是 [default];为什么不统一都用 [default] 或者 [all],至少相当于预热缓存了。
         
     
    logger和appender的关系
     
    主要体现在下面的函数中:
    function getLogger (categoryName) {
     
      // Use default logger if categoryName is not specified or invalid
      if (typeof categoryName !== "string") {
        categoryName = Logger.DEFAULT_CATEGORY;
      }
     
      var appenderList;
      if (!hasLogger(categoryName)) {
        // Create the logger for this name if it doesn't already exist
        loggers[categoryName] = new Logger(categoryName);
        if (appenders[categoryName]) {
          appenderList = appenders[categoryName];
          appenderList.forEach(function(appender) {
            loggers[categoryName].addListener("log", appender);
          });
        }
        if (appenders[ALL_CATEGORIES]) {
          appenderList = appenders[ALL_CATEGORIES];
          appenderList.forEach(function(appender) {
            loggers[categoryName].addListener("log", appender);
          });
        }
      }
     
      return loggers[categoryName];
    }

    其中,loggers 和 appenders 上面说过,一个是是 logger 的缓存map; 一个是 appender 的Map。

     
    getLogger(categoryName)步骤如下:
    1,先去 loggers 中以 categoryName 为 key 找 logger,找到就直接返回,没有找到就生成一个 new Logger(categoryName),并添加到 logger 缓存。 
    2,在 appenders 中以 categoryName 为key 查找 appenderList,找到了就以该 logger 为宿主,将 appenderList 中所有的 appender 添加为 on 事件的监听器。
    3,不管2是否成功,都将 appenders 中 [all] 对应的所有 appender 添加为该 logger 的 on 事件的监听器。
     
    最终两个Map的结构大致如下:
     
    appenders : 
    [all]            ->  [apd1, apd2, ...]
    category1    ->  [apd3]
    category2    ->  [apd4, apd5]
    ...
     
    loggers : 
    [all]            ->  loggerA      --addListener()-->  [apd1, apd2, ...]
    [default]     ->  loggerB      --addListener()-->  [apd1, apd2, ...]
    category1   ->  loggerC      --addListener()-->  [apd3, apd1, apd2, ...]
    category2   ->  loggerD      --addListener()-->  [apd4, apd5, apd1, apd2, ...]
    ...
     
     
    因此:
    1,category 一样的 appender 会同时输出日志;
    2,没有指定 category 的 appender 总是会输出日志;
  • 相关阅读:
    VScode 修改中文字体
    missing KW_END at ')' near '<EOF>'
    SQL inner join, join, left join, right join, full outer join
    SQL字符替换函数translater, replace
    SQL COOKBOOK SQL经典实例代码 笔记第一章代码
    sqlcook sql经典实例 emp dept 创建语句
    dateutil 2.5.0 is the minimum required version python
    安装postgresql后找不到服务 postgresql service
    Postgres psql: 致命错误: 角色 "postgres" 不存在
    【西北师大-2108Java】第十六次作业成绩汇总
  • 原文地址:https://www.cnblogs.com/jasonxuli/p/6518650.html
Copyright © 2011-2022 走看看