java-5.3 log

118 阅读3分钟

需求

image.png 从这段描述可以看出,SLF4J是一个“日志门面”(Logging Facade)它允许用户自由的切换所需的日志框架。

其实SLF4J是典型的面向接口的编程,只是一个日志标准,只提供了一套记录日志的api,并没有日志系统的具体实现,而是通过对接如log4j、java.util.logging等日志框架,来实现日志的输出,各个应用程序在使用日志的的地方全部使用slf4j-api 提供的接口进行编程,系统中具体使用哪个日志框架觉得于引入哪个具体的实现。

设计

image.png

上图中所有的和日志框架的集成都是由SLF4J团队开发的,但是和log4j集成的是log4j1,如果使用log4j2需要使用 log4j-slf4j-impl ,此包为log4j团队实现,

实现

SLF4J 1.8之后实现方式,1.8之后采用Java SPI机制来加载不同的日志实现,由于采用了SPI机制,所以SLF4J要求JDK版本要在1.6以上

  1. 在使用SLF4J开发时我们使用以下代码,可以看出主要涉及两个类:org.slf4j.Logger 和 org.slf4j.LoggerFactory ,通过源码可以看出Logger是一个接口,定义来标准的日志操作,由 LoggerFactory 来具体确定Logger实现,所以此处主要分析 LoggerFactory代码。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}
  1. getLogger 方法主要有两个步骤
  • 获取 ILoggerFactory 的具体实现
  • 通过 ILoggerFactory 的 getLogger 方法获取真实绑定的日志框架,查看 ILoggerFactory 源码发现它是一个接口并且只有 getLogger 一个方法,所以可以判断在正式的日志框架集成的包中需要实现此接口,并返回适配好的 Logger 的实现,接下来分析 getILoggerFactory() 方法,分析SLF4J具体是怎么查找并绑定到具体的日志框架的
public static Logger getLogger(String name) {
    // 获取真实的 ILoggerFactory 的具体实现
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}
  1. 可以看到最终是调用方法 performInitialization(); 执行初始化,而 performInitialization() 方法主要是调用 bind() 方法执行最终的绑定的,如果绑定成功后直接调用 getSingleton() 获取单例,然后调用 getLoggerFactory(); 方法获取具体的实现工厂
public static ILoggerFactory getILoggerFactory() {
    // 判断初始化状态
    // 如果状态 = 未初始化
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                // 修改状态为初始化中
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                // 执行初始化
                performInitialization();
            }
        }
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
		// 绑定成功后直接调用 getSingleton() 获取单例,然后调用 getLoggerFactory(); 方法获取具体的实现工厂
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
    case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://jira.qos.ch/browse/SLF4J-97
        return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}
// 执行初始化 
private final static void performInitialization() {
    bind();
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
		// 绑定成功后,执行版本检查,检查SLF4J版本和具体实现的版本是否一致
        versionSanityCheck();
    }
}
  1. 通过源码可以看出,1.8之后定义了接口 org.slf4j.spi.SLF4JServiceProvider 用于实现SPI的绑定
private final static void bind() {
    try {
        // 使用JavaSPI机制加载具体实现
        List<SLF4JServiceProvider> providersList = findServiceProviders();
        // 如果发现多个绑定 报告发现多个日志实现
        reportMultipleBindingAmbiguity(providersList);
        // 判断具体实现
        if (providersList != null && !providersList.isEmpty()) {
            // 获取第一个绑定
           PROVIDER = providersList.get(0);
           // SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
            // 初始化
           PROVIDER.initialize();
           // 变更状态
           INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
           // 报告具体绑定的日志实现
            reportActualBinding(providersList);
        } else {
            // 如果没有具体实现 修改状态为没有找到具体实现
            INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
            Util.report("No SLF4J providers were found.");
            Util.report("Defaulting to no-operation (NOP) logger implementation");
            Util.report("See " + NO_PROVIDERS_URL + " for further details.");

            // 调用1.7 之前的方法,但是不会进行绑定,只是输出提示
            Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
            reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
        }
        postBindCleanUp();
    } catch (Exception e) {
        failedBinding(e);
        throw new IllegalStateException("Unexpected initialization failure", e);
    }
}
// 标准Java SPI查找实现
private static List<SLF4JServiceProvider> findServiceProviders() {
    ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class);
    List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>();
    for (SLF4JServiceProvider provider : serviceLoader) {
        providerList.add(provider);
    }
    return providerList;
}
  1. 以下为log4j1 集成源码:

image.png org.slf4j.spi.SLF4JServiceProvider

org.slf4j.log4j12.Log4j12ServiceProvider