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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值