JAVA日志体系结构

commons-logging和slf4j都是日志的接口,供用户使用,而没有提供实现!
log4j,logback等等才是日志的真正实现。
  当我们调用接口时,接口的工厂会自动寻找恰当的实现,返回一个实现的实例给我服务。这些过程都是透明化的,用户不需要进行任何操作!

首先日志的用法很简单,通过工厂factory获取log对象,然后打印消息就可以了:
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogTest {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(LogTest.class);
        logger.info("hello {}",new Date());
    }
}
这里也可以看到Slf4j的一个很重要的特性,占位符!—— {} 可以任意的拼接字符串,自动的填入字符串中!

slf4j源码分析:
首先,直接用LoggerFactory的静态工厂获取一个Logger对象,我们先看下getLogger方法!
public static Logger getLogger (Class clazz) {
     return getLogger (clazz.getName());
}
这里把传入的类,提取出名字,再填写到getLogger静态方法中!?为什么要获取类的名字,而根据名字来获取对象呢。因为每个类使用的日志处理实现可能不同,iLoggerFactory中也是根据名字来判断一个类的实现方式的。
public static Logger getLogger (String name) {
     ILoggerFactory iLoggerFactory = getILoggerFactory() ;
     return iLoggerFactory.getLogger(name);
}
在getLogger方法中,通过 getILoggerFactory 获取工厂,然后获取日志对象!看来一切的迷雾都在getILoggerFactory()中!

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
      INITIALIZATION_STATE = ONGOING_INITIALIZATION;
       performInitialization ();
    }
    switch (INITIALIZATION_STATE) {
      case SUCCESSFUL_INITIALIZATION:
        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.
        return TEMP_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
  }
}

第2行~第5行:判断是否进行初始化,如果没有初始化,则修改状态,进入performIntialization初始化!
第6行~第17行:对状态进行测试,如果初始化成功,则通过StaticLoggerBinder获取日志工厂!

? Slf4j如何进行初始化,又是如何获取日志工厂的!
private final static void performInitialization() {
     bind();
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
         versionSanityCheck();
    }
}
在初始化中,先bind(),在修改状态,进行版本检查!

版本检查!
  private final static void versionSanityCheck () {
    try {
      String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
      boolean match = false;
      for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
        if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
          match = true;
        }
      }
      if (!match) {
        Util.report("The requested version " + requested
                + " by your slf4j binding is not compatible with "
                + Arrays.asList(API_COMPATIBILITY_LIST).toString());
        Util.report("See " + VERSION_MISMATCH + " for further details.");
      }
    } catch (java.lang.NoSuchFieldError nsfe) {
      // given our large user base and SLF4J's commitment to backward
      // compatibility, we cannot cry here. Only for implementations
      // which willingly declare a REQUESTED_API_VERSION field do we
      // emit compatibility warnings.
    } catch (Throwable e) {
      // we should never reach here
      Util.report("Unexpected problem occured during version sanity check", e);
    }
  }
这里获取JDK的版本,并与Slf4j支持的版本进行比较,如果大版本相同则通过,如果不相同,那么进行失败提示!

最关键的要看 bind 是如何实现的!
1 private final static void bind() {   //  方法的主要功能就是寻找实现类
2     try {
3        Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet() ;
4       reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
5       // the next line does the binding
6        StaticLoggerBinder.getSingleton();
7        INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
8       reportActualBinding(staticLoggerBinderPathSet);
9       fixSubstitutedLoggers();
10     } catch (NoClassDefFoundError ncde) {
11       String msg = ncde.getMessage();
12       if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
13         INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
14         Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
15         Util.report("Defaulting to no-operation (NOP) logger implementation");
16         Util.report("See " + NO_STATICLOGGERBINDER_URL
17                 + " for further details.");
18       } else {
19         failedBinding(ncde);
20         throw ncde;
21       }
22     } catch (java.lang.NoSuchMethodError nsme) {
23       String msg = nsme.getMessage();
24       if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
25         INITIALIZATION_STATE = FAILED_INITIALIZATION;
26         Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
27         Util.report("Your binding is version 1.5.5 or earlier.");
28         Util.report("Upgrade your binding to version 1.6.x.");
29       }
30       throw nsme;
31     } catch (Exception e) {
32       failedBinding(e);
33       throw new IllegalStateException("Unexpected initialization failure", e);
34     }
35   }

第2行~第10行:初始化!首先获取实现日志的加载路径,查看路径是否合法,再初始化StaticLoggerBinder的对象,寻找合适的实现方式使用。
第10行~第22行:如果找不到指定的类,就会报错!
第22行~第31行:如果找不到指定的方法,就会报错!
第31行~第34行:报错!


如何查找实现类呢?这就要看 findPossibleStaticLoggerBinderPathSet方法了:
1 private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
2
3 private static Set<URL> findPossibleStaticLoggerBinderPathSet() {
4      // use Set instead of list in order to deal with bug #138
5     // LinkedHashSet appropriate here because it preserves insertion order during iteration
6     Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
7     try {
8         ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
10        Enumeration<URL> paths;
11             if (loggerFactoryClassLoader == null) {
12            paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
13             } else {
14               paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
16                        }
17            while (paths.hasMoreElements()) {
18              URL path = (URL) paths.nextElement();
19                        staticLoggerBinderPathSet.add(path);
20                       }
21           } catch (IOException ioe) {
22                        Util.report("Error getting resources from path", ioe);
23                     }
24                     return staticLoggerBinderPathSet;
25 }
slf4j的源码精华之处!
第1行:它定义了一个字符串,这个字符串是实现类的class地址。然后通过类加载加载指定的文件!
第6行:创建一个Set,因为有可能有多个实现。
第8行~第9行:获取LoggerFactory的类加载器!
第11行~第13行:如果获取不到类加载器则说明是系统加载器,那么在系统路径下获取该资源文件
第13行~第15行:获取到了类加载器,则用该类加载器加载指定的资源文件。
第17行~第20行:解析类加载器的地址。
第24行:返回加载器地址的集合。

总结:
    简单的说下它的原理,就是通过工厂类,提供一个用户的接口!用户可以通过这个外观接口,直接使用API实现日志的记录。而后面的具体实现由Slf4j来寻找加载.寻找的过程,就是通过类加载加载那个叫org/slf4j/impl/StaticLoggerBinder.class的文件,只要实现了这个文件的日志实现系统,都可以作为一种实现方式。如果找到很多种方式,那么就寻找一种默认的方式。


slf4j源码解析


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值