jdk-logging log4j1 log4j2 logback 日志框架介绍

来源:互联网 发布:linux 关闭acl 编辑:程序博客网 时间:2024/05/21 10:47

1. 日志框架

目前的日志框架有:

  1. jdk-logging
  2. log4j1
  3. log4j2
  4. logback

用于实现日志统一的框架有:

  1. commons-logging
  2. slf4j

1. jdk-logging

使用方法

1.Demo

package com.jeiker.demo.controller;import java.util.logging.Logger;/** * @Author : xiao * @Date : 17/3/15 下午7:47 */public class JdkTest {    private static final Logger logger = Logger.getLogger(JdkTest.class.getName());    public static void main(String[] args){        logger.info("jdk logging info: a msg");    }}

简单分析

(可跳过)

1.创建LogManager

默认是java.util.logging.LogManager,但是也可以自定义。

源码如下(manager就是LogManager):

try {   cname = System.getProperty("java.util.logging.manager");   if (cname != null) {       try {           Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);           manager = (LogManager) clz.newInstance();       } catch (ClassNotFoundException ex) {           Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);           manager = (LogManager) clz.newInstance();       }   }} catch (Exception ex) {   System.err.println("Could not load Logmanager \"" + cname + "\"");   ex.printStackTrace();} if (manager == null) {   manager = new LogManager();}

2.加载配置文件

默认是jre目录下的lib/logging.properties文件,也可以自定义修改系统属性”java.util.logging.config.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);}catch (Exception ex){}

3.创建Logger

创建Logger,并缓存起来,放置到一个Hashtable中,并把LogManager设置进新创建的logger中。

2. log4j1

使用方法

1.pom文件增加依赖

<dependency>    <groupId>log4j</groupId>    <artifactId>log4j</artifactId>    <version>1.2.17</version></dependency>

依赖的jar包:

  1. log4j-1.2.17.jar

2.配置文件 :log4j.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd"><log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">        <layout class="org.apache.log4j.PatternLayout">            <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>        </layout>    </appender>    <root>        <appender-ref ref="STDOUT" />    </root></log4j:configuration>

3.Demo

package com.jeiker.demo.controller;import org.apache.log4j.LogManager;import org.apache.log4j.Logger;/** * @Author : xiao * @Date : 17/3/15 下午7:29 */public class Log4jTest {    private static final Logger logger = LogManager.getLogger(Log4jTest.class);    public static void main(String[] args){        if(logger.isTraceEnabled()){            logger.debug("log4j trace message");        }        if(logger.isDebugEnabled()){            logger.debug("log4j debug message");        }        if(logger.isInfoEnabled()){            logger.debug("log4j info message");        }    }}

简单分析

(可跳过)

简单的说明获取一个Logger的过程。

分三种情况来说明:

(一)没有指定配置文件路径

  1. 引发LogManager的类初始化

    Logger.getLogger(Log4jTest.class)的源码如下:

    static public Logger getLogger(Class clazz) {  return LogManager.getLogger(clazz.getName());}
  2. 初始化一个logger仓库Hierarchy

    Hierarchy的源码如下:

    public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {    private LoggerFactory defaultFactory;    Hashtable ht;    Logger root;    //其他略}
    • LoggerFactory defaultFactory: 就是创建Logger的工厂
    • Hashtable ht:用来存放上述工厂创建的Logger
    • Logger root:作为根Logger
  3. 在LogManager的类初始化的过程中默认寻找类路径下的配置文件

    • 通过org.apache.log4j.helpers.Loader类来加载类路径下的配置文件:
    Loader.getResource("log4j.xml");Loader.getResource("log4j.properties")
    • 优先选择xml配置文件
  4. 解析上述配置文件

    • 如果是xml文件则org.apache.log4j.xml.DOMConfigurator类来解析

    • 如果是properties文件,则使用org.apache.log4j.PropertyConfigurator来解析

    不再详细说明解析过程,看下解析后的结果:

    • 设置RootLogger的级别

    • 对RootLogger添加一系列我们配置的appender(我们通过logger来输出日志,通过logger中的appender指明了日志的输出目的地)

  5. 获取Logger

    使用logger仓库Hierarchy中内置的LoggerFactory工厂来创建Logger了,并缓存起来,同时将logger仓库Hierarchy设置进新创建的Logger中。

(二)手动加载不在路径下的配置文件

PropertyConfigurator.configure 执行时会去进行上述的配置文件解析。

源码如下:

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

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

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

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

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

3. log4j2

使用方法

1.pom文件增加依赖:

<dependency>    <groupId>org.apache.logging.log4j</groupId>    <artifactId>log4j-api</artifactId>    <version>2.8.1</version></dependency><dependency>    <groupId>org.apache.logging.log4j</groupId>    <artifactId>log4j-core</artifactId>    <version>2.8.1</version></dependency>

依赖的jar包:

  1. log4j-api-2.8.1.jar
  2. log4j-core-2.8.2.jar

    • log4j-api: 作为日志接口层,用于统一底层日志系统
    • log4j-core : 作为上述日志接口的实现,是一个实际的日志框架

2.配置文件: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN">    <Appenders>        <Console name="Console" target="SYSTEM_OUT">            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>        </Console>    </Appenders>    <Loggers>        <Root level="debug">            <AppenderRef ref="Console"/>        </Root>    </Loggers></Configuration>

3.Demo

package com.jeiker.demo.controller;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;/** * @Author : xiao * @Date : 17/3/15 下午7:29 */public class Log4j2Test {    private static final Logger logger= LogManager.getLogger(Log4j2Test.class);    public static void main(String[] args){        if(logger.isTraceEnabled()){            logger.debug("log4j2 trace message");        }        if(logger.isDebugEnabled()){            logger.debug("log4j2 debug message");        }        if(logger.isInfoEnabled()){            logger.debug("log4j2 info message");        }    }}

简单分析

(可跳过)

1. 获取底层使用的LoggerContextFactory

同样LogManager的类加载会去寻找log4j-api定义的LoggerContextFactory接口的底层实现,获取方式有三种:

  • 第一种: 尝试从jar中寻找log4j2.component.properties文件,如果配置了log4j2.loggerContextFactory则使用该LoggerContextFactory

  • 第二种:如果没找到,尝试从jar包中寻找META-INF/log4j-provider.properties文件,如log4j-core-2.2中就有该文件

    如果找到多个,取优先级最高的(该文件中指定了LoggerContextFactory,同时指定了优先级FactoryPriority),如log4j-core-2.2中log4j-provider.properties的文件内容如下:

    LoggerContextFactory = org.apache.logging.log4j.core.impl.Log4jContextFactory Log4jAPIVersion = 2.1.0 FactoryPriority= 10
  • 第三种情况:上述方式还没找到,就使用默认的SimpleLoggerContextFactory

2.使用LoggerContextFactory获取LoggerContext

3.根据LoggerContext获取Logger

以log4j-core为例:

  1. 会首先判断LoggerContext是否被初始化过了,没有则进行初始化
  2. 获取ConfigurationFactory,从配置中获取和插件中获取(log4j-core核心包中有三个YamlConfigurationFactory、JsonConfigurationFactory、XmlConfigurationFactory)
  3. 以上文的案例中,会使用XmlConfigurationFactory来加载log4j2.xml配置文件
  4. LoggerContext初始化后,就可以获取或者创建Logger了

Log4J2 主要对象总结

  • LogManager

    它的类加载会去寻找LoggerContextFactory接口的底层实现,会从jar包中的配置文件中寻找,如上面所述

  • LoggerContextFactory

    用于创建LoggerContext,不同的日志实现系统会有不同的实现,如log4j-core中的实现为Log4jContextFactory

  • PropertyConfigurator

    用于解析log4j.properties文件

  • LoggerContext

    它包含了配置信息,并能创建log4j-api定义的Logger接口实例,并缓存这些实例

  • ConfigurationFactory

    上述LoggerContext解析配置文件,需要用到ConfigurationFactory,目前有三个YamlConfigurationFactory、JsonConfigurationFactory、XmlConfigurationFactory,分别解析yuml json xml形式的配置文件

4. logback

使用方法

1.pom文件增加依赖:

<dependency>    <groupId>ch.qos.logback</groupId>    <artifactId>logback-classic</artifactId>    <version>1.2.1</version></dependency>

依赖的jar包:

  1. slf4j-api-1.7.22.jar
  2. logback-classic-1.2.1.jar
  3. logback-core-1.2.1.jar

2.配置文件:logback.xml

<?xml version="1.0" encoding="utf-8" ?><configuration>    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">        <encoder>            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>        </encoder>    </appender>    <root level="debug">        <appender-ref ref="STDOUT"/>    </root></configuration>

3.Demo:

package com.jeiker.demo.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @Author : xiao * @Date : 17/3/15 下午7:29 */public class LogbackTest {    private static final Logger logger = LoggerFactory.getLogger(LogbackTest.class);    public static void main(String[] args){        if(logger.isDebugEnabled()){            logger.debug("slf4j-logback debug message");        }        if(logger.isInfoEnabled()){            logger.debug("slf4j-logback info message");        }        if(logger.isTraceEnabled()){            logger.debug("slf4j-logback trace message");        }    }}

简单分析

(可跳过)

  • 官方使用方式,slf4j的原生实现,其实就和slf4j集成了起来。

  • 上述的Logger、LoggerFactory都是slf4j自己的接口与类。

  • 没有配置文件的情况下,使用的是默认配置。

1.slf4j与底层的日志系统进行绑定

在jar包中寻找org/slf4j/impl/StaticLoggerBinder.class 这个类,如在logback-classic中就含有这个类。

如果找到多个StaticLoggerBinder,则表明目前底层有多个实际的日志框架,slf4j会随机选择一个。

2.使用上述找到的StaticLoggerBinder创建一个实例,并返回一个ILoggerFactory实例

return StaticLoggerBinder.getSingleton().getLoggerFactory();

以logback-classic中的StaticLoggerBinder为例,在StaticLoggerBinder.getSingleton()过程中,会去加载解析配置文件.

源码如下:

public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {   ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);   //寻找logback.configurationFile的系统属性   URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);   if (url != null) {     return url;   }   //寻找logback.groovy   url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);   if (url != null) {     return url;   }   //寻找logback-test.xml   url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);   if (url != null) {     return url;   }   //寻找logback.xml   return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);}

目前路径都是定死的,只有logback.configurationFile的系统属性是可以更改的,所以如果我们想更改配置文件的位置(不想放在类路径下),则需要设置这个系统属性:

System.setProperty("logback.configurationFile", "/path/to/config.xml");

解析完配置文件后,返回的ILoggerFactory实例的类型是LoggerContext(它包含了配置信息)

3.根据返回的ILoggerFactory实例,来获取Logger

就是根据上述的LoggerContext来创建一个Logger,每个logger与LoggerContext建立了关系,并放到LoggerContext的缓存中,就是LoggerContext的如下属性:

private Map<String, Logger> loggerCache;

其实上述过程就是slf4j与其他日志系统的绑定过程。不同的日志系统与slf4j集成,都会有一个StaticLoggerBinder类,并会拥有一个ILoggerFactory的实现。

0 0
原创粉丝点击