zoukankan      html  css  js  c++  java
  • Log4j源码分析

    一、slf4jlog4j的关系:

     

    也就是说slf4j仅仅是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如JDBC一样,只是一种规则而已。必须搭配具体的log实现方案比如说log4j jdklogging等等,中间需要适配层做桥接,例如slf4jlog4j的桥接包: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.infologger.error等);当调用方调用了info, error等方法记录日志时,调用的是Log4jLoggerAdapter的方法,然后Log4jLoggerAdapter又将调用转给Logger对象去执行;

    2.Appender:

    appender就是日志输出地;日志的记录输出抽象,大致有控制台输出,文件输出等;

    Logger对象在记录日志时,会遍历Logger对象上注册的所有Appender以及父类上注册的Appender,然后依次调用所有AppenderappendLoopOnAppenders方法记录日志;

    主要实现类有WriteAppenderConsoleAppenderFileAppenderWriterAppender将日志写入Java IO中,它继承自SkeletonAppender类。它引入了三个字段:immediateFlush,指定每写完一条日志后,即将日志内容刷新到设备中,因而一般推荐将该值设置为true,即默认值;econding用于定义日志文本的编码方式;qw定义写日志的writer,它可以是文件或是控制台等Java IO支持的流;

    FileAppender的子类主要有DailyRollingFileAppenderRollingFileAppenderDailyRollingFileAppender会在每隔一段时间可以生成一个新的日志文件,不过这个时间间隔是可以设置的,不仅仅只是每隔一天。时间间隔通过setDatePattern()方法设置,datePattern必须遵循SimpleDateFormat中的格式;RollingFileAppender则是基于文件大小作为阀值。当日志文件超过指定大小,日志文件会被重命名成”日志文件名.1”,若此文件已经存在,则将此文件重命名成”日志文件名.2”,一次类推。若文件数已经超过设置的可备份日志文件最大个数,则将最旧的日志文件删除。如果要设置不删除任何日志文件,可以将maxBackupIndex设置成Integer最大值。

    3.Layout:

    实现了OptionHandler的抽象类;主要是对日志行的格式进行限定,常用有PatternLayoutHTMLLayout

    4.LoggerRepository

    常见的Hirearchy为其实现类,封装了框架的默认配置,还有Logger工厂,事件源,封装了一些列事件。

    5.LoggingEvent

    封装了消息内容、级别、记录器类名的全名称等信息;当记录日志时,会将日志内容封装成LoggingEvent对象并调用Logger对象进行记录;

    五、流程图:

    1、调用方调用LoggerFactory.getLogger(XXXX.class)时,首先获取具体日志实现框架的LoggerFactoryLog4jLoggerFactory,在Log4jLoggerFactory.getLogger(XXXX.class)时,首先从hashMap里面去获取,如果获取不到则创建,创建时首先获取getLoggerRepository(也就是Hierarchy),然后在HierarchyhashMap里面去获取,如果找到则直接返回,否则创建新的Logger对象并添加到hashMap里面后返回;

    2、记录日志时调用的info, debug, error等方法都是调用的适配器对象Log4jLoggerAdapter里面的方法,Log4jLoggerAdapter会将调用转到Logger对象上;接着判断Logger实例对应的日志记录级别(每个Logger实例都有自己的Level)是否要比请求的级别低→若是则调用forcedLog记录日志;

    3、接着创建LoggingEvent实例→将LoggingEvent实例传递给appenderAppender调用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

  • 相关阅读:
    初试django
    初试mysql
    ASP.NET MVC 做的网站项目
    Update 更新语句使用两个表关联
    SQL Server 输出所有表结构
    循环数据集字段赋默认值
    FireBird.conf配置文件常用参数
    Delphi 获取临时数据集 ClientDataSet
    DELPHI 读取csv 格式文本文件
    获取 临时数据集 的两种方法
  • 原文地址:https://www.cnblogs.com/laoxia/p/10785458.html
Copyright © 2011-2022 走看看