Java 日志组件(一)

    作为一名Java程序员,日常开发中很定会接触日志系统,但是众多的框架,包括Log4j、Log4j2、Logback、Slf4j、commons-logging等等,引用的maven依赖众多,到底可以去掉哪些,一行Loggerfactory.getLogger(LogbackTest.class)后做了

  哪些工作,怎么去找的配置文件,这还是有很多细节可以研究的。

    目前日志框架有jdk自带的logging、log4j、logback,目前用于实现日志统一的框架Apache的commons-logging、slf4j。为了理清它们的关系与繁杂的各种集成jar包如下:

      • log4j、log4j-api、log4j-core
      • log4j-1.2-api、log4j-jcl、log4j-slf4j-impl、log4j-jul
      • logback-core、logback-classic、logback-access
      • commons-logging
      • slf4j-api、slf4j-log4j12、slf4j-simple、jcl-over-slf4j、slf4j-jdk14、log4j-over-slf4j、slf4j-jcl

1、jdk自带的logging

  1.1、使用案例:

1 private static final Logger logger=Logger.getLogger(JdkLoggingTest.class.getName());
2 
3 public static void main(String[] args){
4     logger.info("jdk logging info: a msg");
5 }

    其中的Logger是:java.util.logging.Logger

  1.2、简单的过程分析:

  • 创建一个LogManager

   默认是java.util.logging.LogManager,但是也可以自定义,修改系统属性“java.util.logging.manager”即可,源码如下:

 1 try {
 2     cname = System.getProperty("java.util.logging.manager");
 3     if (cname != null) {
 4         try {
 5             Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
 6             manager = (LogManager) clz.newInstance();
 7         } catch (ClassNotFoundException ex) {
 8             Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
 9             manager = (LogManager) clz.newInstance();
10         }
11     }
12 } catch (Exception ex) {
13     System.err.println("Could not load Logmanager \"" + cname + "\"");
14     ex.printStackTrace();
15 }
16 if (manager == null) {
17     manager = new LogManager();
18 }
  • 加载配置文件

     默认是jre目录下的lib/logging.properties文件,也可以自定义修改系统属性“java.util.logging.file”,源码如下:

String fname = System.getProperty("java.util.logging.config.file");
if (fname == null) {
    fname = System.getProperty("java.home");
    if (fname == null) {
        throw new Error("Can't find java.home ??");
    }
    File f = new File(fname, "lib");
    f = new File(f, "logging.properties");
    fname = f.getCanonicalPath();
}
InputStream in = new FileInputStream(fname);
BufferedInputStream bin = new BufferedInputStream(in);
try {
    readConfiguration(bin);
}
  •  创建logger,并缓存起来,放置到一个Hashtable中,并把LogManager设置进新创建的logger中。

  以tomcat为例,它就自定义了上述配置:

  在tomcat的启动文件中catalina.bat中,有如下设置:

  •  修改属性"java.util.logging.manager",自定义LogManager,使用自己的ClassLoaderLogManager
1 if not "%LOGGING_MANAGER%" == "" goto noJuliManager
2 set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
3 :noJuliManager
4 set JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%
  • 修改属性"java.util.logging.config.file",自定义配置文件,使用自己的%CATALINA_BASE%\conf\logging.properties文件
1 if not "%LOGGING_CONFIG%" == "" goto noJuliConfig
2 set LOGGING_CONFIG=-Dnop
3 if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig
4 set LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
5 :noJuliConfig
6 set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%

  所以如果想研究tomcat的日志,可以从上面入手。

2、log4j

  2.1、使用案例

  log4j的maven依赖

1 <dependency>
2     <groupId>log4j</groupId>
3     <artifactId>log4j</artifactId>
4     <version>1.2.17</version>
5 </dependency>

  编写log4j.properties配置文件,放到类路径下

1 log4j.rootLogger = debug, console
2 log4j.appender.console = org.apache.log4j.ConsoleAppender
3 log4j.appender.console.layout = org.apache.log4j.PatternLayout
4 log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %m%n

  示例代码:

 1 public class Log4jTest {
 2     private static final Logger logger=Logger.getLogger(Log4jTest.class);
 3     public static void main(String[] args){
 4         if(logger.isTraceEnabled()){
 5             logger.debug("log4j trace message");
 6         }
 7         if(logger.isDebugEnabled()){
 8             logger.debug("log4j debug message");
 9         }
10         if(logger.isInfoEnabled()){
11             logger.debug("log4j info message");
12         }
13     }
14 }

  补充:上述方式默认到类路径下加载log4j.properties配置文件,如果配置文件不在类路径下,可以选择如下加载方式来加载配置文件:

1 //使用classLoader来加载资源
2 PropertyConfigurator.configure(Log4jTest.class.getClassLoader().getResource("properties/log4j.properties"));
3 
4 //使用log4j自带的Loader来加载资源
5 PropertyConfigurator.configure(Loader.getResource("properties/log4j.properties"));

  2.1、获取Logger的原理  

  简单说明Logger获取的过程:

  • 没有指定配置文件路径

    1、LogManager初始化:

1 static public Logger getLogger(Class clazz) {
2     return LogManager.getLogger(clazz.getName());
3 }

    2、初始化一个Logger仓库Hierarchy

1 public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
2       private LoggerFactory defaultFactory;
3       Hashtable ht;
4       Logger root;
5     //其他略
6 }

    LoggerFactory defaultFactory就是创建的Logger工厂

    Hashtable ht用来存放上述工厂创建的Logger

    Logger root作为根Logger

    LoggerManager在初始化的时候使用如下方式初始化Hierarchy

1 static {
2     Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
3     //
4 }

    new RootLogger作为root logger,默认是debug级别,最后把Hierarchy绑定到LogManager上,可以在任何地方来获取这个logger仓库Hierarchy

    3、在LogManager的类初始化的过程中默认寻找类路径下的配置文件,优先加载xml配置

1 Loader.getResource("log4j.xml");
2 Loader.getResource("log4j.properties")

    4、解析上述配置文件

    5、获取Logger

  • 手动加载不在类路径下的配置文件

    PropertyConfigurator.configure 执行时会去进行上述的配置文件解析,源码如下:

1 public static void configure(java.net.URL configURL) {
2      new PropertyConfigurator().doConfigure(configURL,
3                         LogManager.getLoggerRepository());
4 }
    • 仍然先会引发LogManager的类加载,创建出logger仓库Hierarchy,同时尝试加载类路径下的配置文件,此时没有则不进行解析,此时logger仓库Hierarchy中的RootLogger默认采用debug级别,没有appender而已。

    • 然后解析配置文件,对上述logger仓库Hierarchy的RootLogger进行级别的设置,添加appender

    • 此时再去调用Logger.getLogger,不会导致LogManager的类初始化(因为已经加载过了)

  • 配置文件在类路径下,而我们又手动使用PropertyConfigurator去加载

      也就会造成2次加载解析配置文件,仅仅会造成覆盖而已(对于RootLogger进行从新设置级别,删除原有的appender,重新加载新的appender),所以多次加载解析配置文件以最后一次为准。

  2.3、主要对象的总结

      • LogManager: 它的类加载会创建logger仓库Hierarchy,并尝试寻找类路径下的配置文件,如果有则解析

      • Hierarchy : 包含三个重要属性:

        • LoggerFactory logger的创建工厂
        • Hashtable 用于存放上述工厂创建的logger
        • Logger root logger,用于承载解析文件的结果,设置级别,同时存放appender
      • PropertyConfigurator: 用于解析log4j.properties文件

      • Logger : 我们用来输出日志的对象

posted @ 2018-12-11 16:25  德克济克Dekjike  阅读(743)  评论(0编辑  收藏  举报