• 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 总是会输出日志;
  • 相关阅读:
    IE8、IE9解决浏览器跨域。
    英语写作-Introduction
    qt添加图标
    Qt 编译错误 :cannot find file .pro
    python
    数据集
    基金
    visio2010求交操作
    书籍网站
    ROS安装xtion
  • 原文地址:https://www.cnblogs.com/jasonxuli/p/6518650.html
Copyright © 2020-2023  润新知