log4j与slf4j源码解析

1.log4j与slf4j的关系

 SLF4J,即简单日志门面(Simple Logging Facade for Java)。从设计模式的角度考虑,它是用来在log和代码层之间起到门面的作用。对用户来说只要使用slf4j提供的接口,即可隐藏日志的具体实现。这与jdbc和相似。使用jdbc也就避免了不同的具体数据库。使用了slf4j可以对客户端应用解耦。因为当我们在代码实现中引入log日志的时候,用的是接口,所以可以实时的更具情况来调换具体的日志实现类。这就是slf4j的作用。

SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。

    gghjgjh

配置SLF4J是非常简单的一件事,只要将与你打算使用的日志系统对应的jar包加入到项目中,SLF4J就会自动选择使用你加入的日志系统。

2.类的基本继承结构

    mavne配置

    <!-- slf4j -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.12</version>
    </dependency>

    <!-- slf4j-log4j -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.12</version>
    </dependency>

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

一个简单的例子

package demos;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author:王喜
 * @Description :Slf4j 日志门面接口 Test
 * @Date: 2018/4/19 0019 13:04
 */
public class Slf4jFacadeTest {

    private final static Logger LOGGER = LoggerFactory.getLogger(Slf4jFacadeTest.class);

    public static void main(String[] args) {
        if(LOGGER.isDebugEnabled()){
            LOGGER.debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>slf4j-log4j debug message");
        }
        if(LOGGER.isInfoEnabled()){
            LOGGER.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<slf4j-log4j info message");
        }
        if(LOGGER.isTraceEnabled()){
            LOGGER.trace("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<slf4j-log4j trace message");
        }

        System.out.println(Slf4jFacadeTest.class);
        System.out.println(Slf4jFacadeTest.class.getName());
    }
}

运行结果如下。日志级别下面会有讲到。

"C:\Program Files\Java\jdk1.8.0_144\bin\java" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2017.1.2\lib\idea_rt.jar=60546:D:\Program Files\JetBrains\IntelliJ IDEA 2017.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;E:\workSpaceJava\maven_demos\target\classes;E:\web-repository\org\slf4j\slf4j-api\1.7.12\slf4j-api-1.7.12.jar;E:\web-repository\org\slf4j\slf4j-log4j12\1.7.12\slf4j-log4j12-1.7.12.jar;E:\web-repository\log4j\log4j\1.2.17\log4j-1.2.17.jar" demos.Slf4jFacadeTest
2018-04-19 19:58:27 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>slf4j-log4j debug message
2018-04-19 19:58:27 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<slf4j-log4j info message
2018-04-19 19:58:27 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<slf4j-log4j trace message
class demos.Slf4jFacadeTest
demos.Slf4jFacadeTest

首先,Logger是slf4j包的接口,LoggerFactory是slf4j下面的类,而且在这个类中有这么一个static方法

public static ILoggerFactory getILoggerFactory() {
    if(INITIALIZATION_STATE == 0) {
        INITIALIZATION_STATE = 1;
        performInitialization();
    }
    //初始化判断
    switch(INITIALIZATION_STATE) {
    case 1:
        return TEMP_FACTORY;
    case 2:
        throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
    case 3:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case 4:
        return NOP_FALLBACK_FACTORY;
    default:
        throw new IllegalStateException("Unreachable code");
    }
}

那么这个工厂类的工厂方法的作用是什么呢?往下看:

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

public static Logger getLogger(Class<?> clazz) {
    Logger logger = getLogger(clazz.getName());
    if(DETECT_LOGGER_NAME_MISMATCH) {
        Class<?> autoComputedCallingClass = Util.getCallingClass();
        if(nonMatchingClasses(clazz, autoComputedCallingClass)) {
            Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", new Object[]{logger.getName(), autoComputedCallingClass.getName()}));
            Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
        }
    }

    return logger;
}

这是LoggerFactory.getLogger()方法源码。获得日志工厂得到Logge。ILoggerFactory是slf4j下面的接口,并且接口下面只有一个方法。

package org.slf4j;

public interface ILoggerFactory {
    Logger getLogger(String var1);
}

通过bind()方法可以绑定具体的日志系统。

主要是通过StaticLoggerBinder类进行绑定

package org.slf4j.impl;

import org.apache.log4j.Level;
import org.slf4j.ILoggerFactory;
import org.slf4j.helpers.Util;
import org.slf4j.spi.LoggerFactoryBinder;

public class StaticLoggerBinder implements LoggerFactoryBinder {
    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    public static String REQUESTED_API_VERSION = "1.6.99";
    private static final String loggerFactoryClassStr = Log4jLoggerFactory.class.getName();
    private final ILoggerFactory loggerFactory = new Log4jLoggerFactory();   //在slf4j包下面的StaticLoggerBind类用Log4jLoggerFactory实现了ILoggerFactory接口

    public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

    private StaticLoggerBinder() {
        try {
            Level var1 = Level.TRACE;
        } catch (NoSuchFieldError var2) {
            Util.report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version");
        }

    }
    //返回log4j对象
    public ILoggerFactory getLoggerFactory() {
        return this.loggerFactory;
    }

    public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
    }
}

具体的绑定方法源码可以查看https://my.oschina.net/xianggao/blog/519199

问题:getLog()为什么要传递一个类的字节码参数?

3.log4j的日志级别(由高到低)

  • ERROR
  • WARN
  • INFO
  • DEBUG
  • TRACE
Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。 
比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。 
程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。 
如果设置级别为INFO,则优先级高于等于INFO级别(如:INFO、WARN、 

ERROR)的日志信息将可以被输出,小于该级别的如DEBUG将不会被输出。

一段简单的配置

log4j.rootLogger = trace, console
log4j.appender.console = org.apache.log4j.ConsoleAppender  //打印到控制台上
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %m%n

猜你喜欢

转载自blog.csdn.net/qq_36387730/article/details/80009535
今日推荐