Log4j源码分析
一、slf4j和log4j的关系:
也就是说slf4j仅仅是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如JDBC一样,只是一种规则而已。必须搭配具体的log实现方案比如说log4j jdklogging等等,中间需要适配层做桥接,例如slf4j与log4j的桥接包:slf4j-log4j12-1.6.1.jar。
二、log4j加载过程:
代码中的用法一般如下:
Logger myLog = LoggerFactory.getLogger(XXXX.class);\ myLog.info(“this is info log text”); myLog.error(“this is error log text”); |
1、获取Logger对象步骤:
(1) 获取StaticLoggerBinder的单例对象;
(2) StaticLoggerBinder对象里有一个Log4jLoggerFactory对象,Log4jLoggerFactory对象里面有一个存储了Logger对象的hash表:
loggerMap = new ConcurrentHashMap<String, Logger>(); |
(3) getLogger(XXXX.class)时首先查这个hash表,如果查询到则直接返回,否则创建Log4jLoggerAdapter对象并添加到这个hash表中;
2、日志记录:
(1) Log4jLoggerAdapter对象作为Logger对象的代理对象,所有记录日志的info, error等方法都是传递到Logger对象去执行处理的;
三、Log4j配置文件解析过程:
Log4jLoggerFactory在构造函数中会调用LogManager的getRootLogger方法,LogManager的静态初始方法块中会检查配置文件并加载,可以是指定的Class类,也可以是xml格式配置文件,也可以是properties格式的配置文件;对于properties格式的配置文件,使用PropertyConfigurator类来读取和解析配置信息;
Properties格式的log4j的配置文件说明:
#Log4J配置文件实现了输出到控制台、文件、回滚文件、自定义标签,数据库等功能。仅供参考。 log4j.rootLogger=DEBUG,CONSOLE,FILE,DLOGFILE,ROLLING_FILE,MYSQL_LOG log4j.addivity.org.apache=true #应用于控制台 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.Threshold=DEBUG log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyyMMdd-HH:mm:ss} %t %c %m%n #应用于文件 log4j.appender.FILE=org.apache.log4j.FileAppender log4j.appender.FILE.File=d:\\file.log log4j.appender.FILE.Append=false log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=%d{yyyyMMdd-HH:mm:ss} %t %c %m%n #应用于按日期生成文件 log4j.appender.DLOGFILE=org.apache.log4j.DailyRollingFileAppender log4j.appender.DLOGFILE.File=d:\\test.log log4j.appender.DLOGFILE.Threshold=INFO log4j.appender.DLOGFILE.DatePattern='.'yyyy-MM-dd log4j.appender.DLOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.DLOGFILE.layout.ConversionPattern=%d{yyyyMMdd-HH:mm:ss} %t %c %m%n #应用于文件回滚 log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.Threshold=INFO log4j.appender.ROLLING_FILE.File=d:\\rolling.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=1KB log4j.appender.ROLLING_FILE.MaxBackupIndex=1 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern=%d{yyyyMMdd-HH:mm:ss} %t %c %m%n # 数据库输出 log4j.appender.MYSQL_LOG=org.apache.log4j.jdbc.JDBCAppender log4j.appender.MYSQL_LOG.driver=com.mysql.jdbc.Driver log4j.appender.MYSQL_LOG.URL=jdbc:mysql://127.0.0.1:3306/txl log4j.appender.MYSQL_LOG.Threshold=ERROR log4j.appender.MYSQL_LOG.user=root log4j.appender.MYSQL_LOG.password= log4j.appender.MYSQL_LOG.sql=insert into log_monitor(level,category,thread,time,location,note) values('%p','%c','%t','%d{yyyy-MM-dd HH:mm:ss:SSS}','%l','%m') log4j.appender.MYSQL_LOG.layout=org.apache.log4j.PatternLayout #虽然以上布局 没啥效果,但是可以减少告警提示 #自定义Appender ,输出到任意地方 |
四、Log4j组件:
1.Logger:
继承Category(一种日志类),提供不同级别的日志接口(例:logger.info,logger.error等);当调用方调用了info, error等方法记录日志时,调用的是Log4jLoggerAdapter的方法,然后Log4jLoggerAdapter又将调用转给Logger对象去执行;
2.Appender:
appender就是日志输出地;日志的记录输出抽象,大致有控制台输出,文件输出等;
Logger对象在记录日志时,会遍历Logger对象上注册的所有Appender以及父类上注册的Appender,然后依次调用所有Appender的appendLoopOnAppenders方法记录日志;
主要实现类有WriteAppender,ConsoleAppender,FileAppender;WriterAppender将日志写入Java IO中,它继承自SkeletonAppender类。它引入了三个字段:immediateFlush,指定每写完一条日志后,即将日志内容刷新到设备中,因而一般推荐将该值设置为true,即默认值;econding用于定义日志文本的编码方式;qw定义写日志的writer,它可以是文件或是控制台等Java IO支持的流;
FileAppender的子类主要有DailyRollingFileAppender和RollingFileAppender:DailyRollingFileAppender会在每隔一段时间可以生成一个新的日志文件,不过这个时间间隔是可以设置的,不仅仅只是每隔一天。时间间隔通过setDatePattern()方法设置,datePattern必须遵循SimpleDateFormat中的格式;RollingFileAppender则是基于文件大小作为阀值。当日志文件超过指定大小,日志文件会被重命名成”日志文件名.1”,若此文件已经存在,则将此文件重命名成”日志文件名.2”,一次类推。若文件数已经超过设置的可备份日志文件最大个数,则将最旧的日志文件删除。如果要设置不删除任何日志文件,可以将maxBackupIndex设置成Integer最大值。
3.Layout:
实现了OptionHandler的抽象类;主要是对日志行的格式进行限定,常用有PatternLayout和HTMLLayout;
4.LoggerRepository:
常见的Hirearchy为其实现类,封装了框架的默认配置,还有Logger工厂,事件源,封装了一些列事件。
5.LoggingEvent:
封装了消息内容、级别、记录器类名的全名称等信息;当记录日志时,会将日志内容封装成LoggingEvent对象并调用Logger对象进行记录;
五、流程图:
1、调用方调用LoggerFactory.getLogger(XXXX.class)时,首先获取具体日志实现框架的LoggerFactory类Log4jLoggerFactory,在Log4jLoggerFactory.getLogger(XXXX.class)时,首先从hashMap里面去获取,如果获取不到则创建,创建时首先获取getLoggerRepository(也就是Hierarchy),然后在Hierarchy的hashMap里面去获取,如果找到则直接返回,否则创建新的Logger对象并添加到hashMap里面后返回;
2、记录日志时调用的info, debug, error等方法都是调用的适配器对象Log4jLoggerAdapter里面的方法,Log4jLoggerAdapter会将调用转到Logger对象上;接着判断Logger实例对应的日志记录级别(每个Logger实例都有自己的Level)是否要比请求的级别低→若是则调用forcedLog记录日志;
3、接着创建LoggingEvent实例→将LoggingEvent实例传递给appender,Appender调用Layout实例格式化日志消息;最后Appender将格式化后的日志信息写入改Appender对应的日志输出中。
六、相关问题:
1.如何实现log4j和slf4j的解耦:也即如何实现将log4j的实现绑定到slf4j的接口定义上?
LoggerFactory的绑定:
LoggerFactory是一个定义在slf4j-api中的类,其getLogger通过StaticLoggerBinder的单实例对象获取到ILoggerFactory对象,StaticLoggerBinder是在slf4j-log4j插件里面定义的;最后获取Logger对象是在LogManager中创建Logger对象并返回的,LogManager和Logger对象都是在实现jar包log4j里面实现的;
2.Appender是如何注册到Logger上的?
PropertyConfigurator类的parseCategory方法解析配置文件log4j.properties的代码:
void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) { ................... // Begin by removing all existing appenders. logger.removeAllAppenders(); Appender appender; String appenderName; while(st.hasMoreTokens()) { appenderName = st.nextToken().trim(); if(appenderName == null || appenderName.equals(",")) continue; LogLog.debug("Parsing appender named \"" + appenderName +"\"."); appender = parseAppender(props, appenderName); if(appender != null) { logger.addAppender(appender); } } } |
LogManager的静态代码中检测到配置文件log4j.properties的路径并加载这个配置文件,然后执行parseCategory方法解析配置文件;上面加粗部分,parseAppender会根据log4j.properties配置文件生成appender对象,然后将其添加到logger对象上;
七、参考资料:
https://my.oschina.net/xianggao/blog/518059
https://www.cnblogs.com/zeng-wei/archive/2012/08/28/2660363.html
https://blog.csdn.net/m0_37652164/article/details/80487522
https://blog.csdn.net/u011794238/article/details/50736331/
http://wiki.10101111.com/pages/viewpage.action?pageId=190240215
http://www.cnblogs.com/question-sky/p/7425069.html
http://www.cnblogs.com/question-sky/p/7429548.html
http://www.cnblogs.com/question-sky/p/7469596.html
https://www.cnblogs.com/question-sky/p/8436366.html
http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html
http://www.blogjava.net/DLevin/archive/2012/11/04/390755.html
Log4j源码分析的更多相关文章
- java 日志体系(四)log4j 源码分析
java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...
- commons-logging + log4j源码分析
分析之前先理清楚几个概念 Log4J = Log For Java SLF4J = Simple Logging Facade for Java 看到Facade首先想到的就是设计模式中的门面(Fac ...
- Log4j漏洞源码分析
Log4j漏洞源码分析 这几天Log4j的问题消息满天飞,今天我们就一起来看看从源码角度看看这个漏洞是如何产生的. 大家都知道这次问题主要是由于Log4j中提供的jndi的功能. 具体涉及到的入口类是 ...
- YARN DistributedShell源码分析与修改
YARN DistributedShell源码分析与修改 YARN版本:2.6.0 转载请注明出处:http://www.cnblogs.com/BYRans/ 1 概述 2 YARN Distrib ...
- springmvc源码分析
Spring MVC源码分析--初始化过程 标签: springmvcconstructioniocclass 2012-09-09 21:32 26578人阅读 评论(3) 收藏 举报 版权声明:本 ...
- Solr4.8.0源码分析(15) 之 SolrCloud索引深入(2)
Solr4.8.0源码分析(15) 之 SolrCloud索引深入(2) 上一节主要介绍了SolrCloud分布式索引的整体流程图以及索引链的实现,那么本节开始将分别介绍三个索引过程即LogUpdat ...
- Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建
Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 由于公司里的Solr调试都是用远程jpda进行的,但是家里只有一台电脑所以不能jpda进行调试,这是因为jpda的端口冲突.所以 ...
- Nimbus<二>storm启动nimbus源码分析-nimbus.clj
nimbus是storm集群的"控制器",是storm集群的重要组成部分.我们可以通用执行bin/storm nimbus >/dev/null 2>&1 &a ...
- log4j源码解析-文件解析
承接前文log4j源码解析,前文主要介绍了log4j的文件加载方式以及Logger对象创建.本文将在此基础上具体看下log4j是如何解析文件并输出我们所常见的日志格式 附例 文件的加载方式,我们就选举 ...
随机推荐
- eclipse中访问不了tomcat首页server Locations变灰无法编辑
eclipse中访问不了tomcat首页server Locations变灰无法编辑 2014年07月25日 14:37:21 wuha0 阅读数:19139更多 个人分类: servlet 解决 ...
- jquery slideDown 控制div出现的方向
.custom-popup { position: absolute; /*top: 0;*/ 上向下 ; 下向上 ; ; display: none; width: 100%; height: 10 ...
- 利用Excel-Vba进行多表汇总和数据透视表
汇总表格式 详情表格式 要求根据汇总表中的信息,到详情表中查找详细物料的具体个数 最终,对物料的个数进行汇总,结果如下图: ExcelVba代码如下(有一些注释代码供参考) Sub Start() S ...
- html+css+javascript之间的关系与作用
三者间的关系 一个基本的网站包含很多个网页,一个网页由html, css和javascript组成. html是主体,装载各种dom元素:css用来装饰dom元素:javascript控制dom元素. ...
- Firefox下载附件乱码的解决办法
通过在http的header里设置fileName下载附件时,中文文件名通过chrome浏览器下载时正常,通过firefox下载时为乱码: 原来的Java代码: response.addHeader( ...
- Java的类型强制转换
不说基本类型,没什么意思. 小括号的类型转换,在引用上,表示我坚定的确信,该未知类型一定是我转的类型,或者是我转的类型的子类. 这个转换逻辑和基本类型是不一致的.它不会进行任何具体的操作,只是一种标识 ...
- L2-007 家庭房产 (25 分)
L2-007 家庭房产 (25 分) 给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数.人均房产面积及房产套数. 输入格式: 输入第一行给出一个正整数N(≤),随后N行,每行按下 ...
- JAVA对mongodb的基本操作
public class test3 { //连接数据库(不需要验证,用于测试连接本地的mongodb) public static MongoDatabase getDatabase(String ...
- oracle mysql 比较
转载:https://www.cnblogs.com/qq765065332/p/9293029.html 一.数据的存储结构 mysql: 1.对数据的管理可以有很多个用户,登录用户后可以看到该用户 ...
- eclipse使用技巧心得分享
eclipse使用技巧心得分享 习惯了eclipse开发java程序,公司最近的项目都是idea开发的,同时android studio也是idea原型开发的,在学android开发,所以脱离ec ...