本文地址 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 总是会输出日志;

Log4js 工作原理及代码简析的更多相关文章

  1. OpenStack之虚机冷迁移代码简析

    OpenStack之虚机冷迁移代码简析 前不久我们看了openstack的热迁移代码,并进行了简单的分析.真的,很简单的分析.现在天气凉了,为了应时令,再简析下虚机冷迁移的代码. 还是老样子,前端的H ...

  2. jdk1.8 ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数

    ConcurrentHashMap 的工作原理及代码实现: 相比于1.7版本,它做了两个改进 1.取消了segment分段设计,直接使用Node数组来保存数据,并且采用Node数组元素作为锁来实现每一 ...

  3. JAVA NIO工作原理及代码示例

    简介:本文主要介绍了JAVA NIO中的Buffer, Channel, Selector的工作原理以及使用它们的若干注意事项,最后是利用它们实现服务器和客户端通信的代码实例. 欢迎探讨,如有错误敬请 ...

  4. Java三大器之过滤器(Filter)的工作原理和代码演示

    一.Filter简介 Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp,Servlet, 静 ...

  5. ConcurrentHashMap 的工作原理及代码实现

    ConcurrentHashMap采用了非常精妙的"分段锁"策略,ConcurrentHashMap的主干是个Segment数组.Segment继承了ReentrantLock,所 ...

  6. HashMap 的工作原理及代码实现,什么时候用到红黑树

    HashMap工作原理及什么时候用到的红黑树: 在jdk 1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里.但是当位于一个桶中的元素较多,即has ...

  7. WinForm 自动完成控件实例代码简析

    在Web的应用方面有js的插件实现自动完成(或叫智能提示)功能,但在WinForm窗体应用方面就没那么好了. TextBox控件本身是提供了一个自动提示功能,只要用上这三个属性: AutoComple ...

  8. Java三大器之监听器(Listener)的工作原理和代码演示

    现在来说说Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次, ...

  9. uboot 2013.01 代码简析(3)第二阶段初始化

    u-boot第二阶段初始化内容的入口函数是_main,_main位于arch/arm/lib/crt0.S文件中: _main函数中先为调用board_init_f准备初始化环境(设置栈指针sp和并给 ...

随机推荐

  1. (转)从程序员到CTO

    好好努力吧,向优秀的人看齐.文章来自http://blog.csdn.net/smarttony/article/details/6697617

  2. [干货] 有了微信小程序,谁还学ReactNative?

    版权声明:本文由贺嘉原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/145 来源:腾云阁 https://www.qclou ...

  3. Dockerfile分享之SSH Server

    版权声明:本文由姚俊刚原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/84 来源:腾云阁 https://www.qclou ...

  4. intellij IDEA 报 非法字符 \65279 原因及解决方法

    用eclipse创建的项目导入到 intellij IDEA 之后 编译时包 非法字符 '\65279' 该问题产生的原因是 IDEA对以UTF8编码的文件保存时自动加上了BOM(UTF-8文件签名) ...

  5. Git的配置和使用

    eclipse中Git的配置 可以参考http://www.cnblogs.com/zhxiaomiao/archive/2013/05/16/3081148.html, http://blog.cs ...

  6. Asp.net页面生存周期【转】

    ASP.NET 页面生存周期中的关键事件 要想深入ASP.NET页面编程,就必须了解页面生存周期各个阶段及相关事件.重写相关事件和方法可以使我们更好的控制页面呈现. # 事件或方法 功能 描述 1 I ...

  7. What Drives the Need for Database Sharding? DATABASE SHARDING

    wIO瓶颈 http://www.agildata.com/database-sharding/ What Drives the Need for Database Sharding? Databas ...

  8. Linux下批量管理工具pssh使用记录

    pssh是一款开源的软件,使用python实现,用于批量ssh操作大批量机器:pssh是一个可以在多台服务器上执行命令的工具,同时支持拷贝文件,是同类工具中很出色的:比起for循环的做法,我更推荐使用 ...

  9. 大规模Docker平台自动化监控之路

    本文介绍了通过Monitor,如何实现大规模容器运维平台的自动化监控需求. 尽管Docker技术目前还处于不稳定的发展与标准制定阶段,但这门技术已经呈现了极其火热的增长状态,却已经是不争的实事.到底有 ...

  10. pathmunge

    pathmunge是linux系统redhat系列版本系统变量/etc/profile中的函数 判断当前系统的PATH中是否有该命令的目录,如果没有,则判断是要将该目录放于PATH之前还是之后 pat ...