本文地址 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. js jquery获取当前元素的兄弟级 上一个 下一个元素 jquery如何获取第一个或最后一个子元素

    var chils= s.childNodes;  //得到s的全部子节点 var par=s.parentNode;   //得到s的父节点 var ns=s.nextSbiling;   //获得 ...

  2. 我觉得epoll和select最大的区别

    最近在用epoll,网速资料很多,大家都说epoll和select的区别比较大,而且select要不停遍历所有的fd,效率要低,而且fd有限制. 但是我认为二者最大的区别在于 先看代码 while ( ...

  3. window.postMessage跨文档通信

    window.postMessage 1.浏览器兼容情况:IE8+.chrome.firefox等较新浏览器都至此. 2.使用方法: a.otherWindow.postMessage( messag ...

  4. Android 内存泄露总结(附内存检测工具)

    https://segmentfault.com/a/1190000006852540 主要是分三块: 静态储存区:编译时就分配好,在程序整个运行期间都存在.它主要存放静态数据和常量. 栈区:当方法执 ...

  5. 在linux下安装Avria(小红伞)

    1.下载AntiVir PersonalEdition Classic for linux http://www.free-av.com/ 2.解压: tar zxvf antivir.tar.gz ...

  6. Asp SqlDataSource将数据库数据绑定在 GridView

    1.首先认识一下GridView的几条属性 ☻AllowPaging  确定是否可以分页 ☻AllowSorting  确定是否可以进行排序 ☻AlternatingRowStyle  指定奇数行样式 ...

  7. 编程中,static的用法详解

    C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static.前者应用于普通变量和函数,不涉及类:后者主要说明static在类中的作用.一.面向过程设计中的sta ...

  8. 170608、Spring 事物机制总结

    spring两种事物处理机制,一是声明式事物,二是编程式事物 声明式事物 1)Spring的声明式事务管理在底层是建立在AOP的基础之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加 ...

  9. 170504、MongoDB和MySQL对比(译)

    一.概要 几十年来,关系型数据库已经成为企业应用程序的基础,自从MySQL在1995年发布以来,它已经成为一种受欢迎并且廉价的选择.然而随着近年来数据量和数据的不断激增,非关系数据库技术如MongoD ...

  10. DetailView内匿名函数不可用

    DetailView yii\widgets\DetailView 小部件显示的是单一 yii\widgets\DetailView::$model 数据的详情. 它非常适合用常规格式显示一个模型(例 ...