zoukankan      html  css  js  c++  java
  • java的日志知识

    java常用的日志有以下几种 :

    一、jdk自带的java.util.logging包下的日志功能, 不常用。

    二、commons-logging  + log4j 的搭配 。log4j是日志功能的具体实现,而commons-logging则是日志接口的声明,它的出现也是为了解决应用和具体的日志框架解耦合的问题,它采用的是运行时动态绑定的方式来决定使用哪个日志框架。 什么是动态绑定 ?参考commons-logging-1.1.3的org.apache.commons.logging.LogFactory类的方法: 

    public static Log getLog(Class clazz) throws LogConfigurationException {
      return getFactory().getInstance(clazz);
    }

    关键在getFactory()返回的LogFactory(日志工厂) 是什么 !

    我们进一步看获取日志工厂的方法:

    public static LogFactory getFactory() throws LogConfigurationException

    从419-646行:代码比较长不列出,阐述如下: 

    1、获取线程上下文ClassLoader(默认是加载应用的ClassLoader)。 

    2、看线程上下文ClassLoader是否有缓存的LogFactory,有就直接返回LogFactory  。

    3、找classpath下面的commons-logging.properties ,如果use_tccl属性为false,则不使用Thread ContextClassLoader .默认use_tccl 为true ;.

    4、找是否有org.apache.commons.logging.LogFactory 这个系统配置项,利用Thread ContextClassLoader 加载org.apache.commons.logging.LogFactory ,并创建一个LogFactory实例(在后面要描述的jcl-over-slf4j包和commons-logging包里面都有这个类。) 

    5、如果还找不到,则找包含了META-INF/services/org.apache.commons.logging.LogFactory 这个文件的Jar包,找到了就用这个文件里面的LogFactory类名创建LogFactory实例

    6、如果还找不到,则找commons-logging.properties 文件,利用属性文件里面的org.apache.commons.logging.LogFactory 属性获取LogFactory类并创建LogFactory实例。

    7、还找不到,则找org.apache.commons.logging.impl.LogFactoryImpl 这个类 创建实例。

    8、创建好LogFactory实例以后,会

    cacheFactory(contextClassLoader, factory); 

    把LogFactory对象和线程上下文ClassLoader在map中关联起来,加速LogFactory对象的获取。

    从以上的分析来看,我们假设一种简单的场景, 

    没有org.apache.commons.logging.LogFactory 这个系统配置项,classpath下没有包含META-INF/services/org.apache.commons.logging.LogFactory 这个文件的Jar包、没有commons-logging.properties 文件,只有commons-logging和Log4j的jar .

    此时会使用org.apache.commons.logging.impl.LogFactoryImpl 这个类(在commons-logging的jar 包里面) 来创建Log实例,而LogFactoryImpl在获取Log类的时候,会参照下面一个顺序: 

    private static final String[] classesToDiscover = {
    LOGGING_IMPL_LOG4J_LOGGER,
    "org.apache.commons.logging.impl.Jdk14Logger",
    "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
    "org.apache.commons.logging.impl.SimpleLog"
    };

    来使用某一个日志器, 可以看到默认就是使用log4j日志的。 

    至此,我们弄清了commons-logging的动态绑定机制。 

    但是这种机制的问题在哪儿呢,由于它使用了ClassLoader寻找和载入底层的日志库, 导致了象OSGI这样的框架无法正常工作,因为OSGI的不同的插件使用自己的ClassLoader。 OSGI的这种机制保证了插件互相独立,然而却使Apache Common-Logging无法工作。

    三、slf4j + logback的搭配. 

    slf4j和logback是同一个人开发的,所以不像slf4j + log4j搭配使用时,还需要加上一个所谓的适配器jar包(比如:slf4j-log4j.jar,适配器包的作用就是通过class composition的适配方式把log4j的日志转换成slf4j的接口)。

    和commons-logging在运行时确定日志框架不同,slf4j采用的方式是在编译时静态绑定真正的log库, 它的原理是:

       (1)用ClassLoader找包含了org.slf4j.impl.StaticLoggerBinder类的Jar包,这个Jar就包含在logback-classic.jar、slf4j-log4j等包里面 ,因此,如果在classpath下面

    同时包含了这些jar的话, 程序将会出现一个警告,提示classpath下面有多个slf4j的绑定 。这也是我们在使用slf4j日志时需要注意的问题,不过slf4j并不会报错, 而是选择一个

    jar中的StaticLoggerBinder,所以在使用的时候要特别注意不能同时包含logback-classic 、 slf4j-log4j、slf4j-jdk等桥接包 。

    我们假设使用的是logback做日志框架 ,这时会拿到logback-classic.jar里面的org.slf4j.impl.StaticLoggerBinder ,这个StaticLoggerBinder获取到日志工厂以后,

    利用日志工厂获取到ch.qos.logback.classic.Logger, 接下来使用的就是logback的日志了。

    而如果是classpath下面包含的是slf4j-log4j这个桥接包, 那么拿到的就是Log4j的LogFactory,从而也就用到了log4j的日志。

     四、既然slf4j的静态绑定方式解决了commons-logging动态绑定方式在运行时可能拿不到日志接口实现类的问题,而且号称效率比log4j要更好(为什么更好,后续还会深入分析) 那直接都换成slf4j+logback的日志方式不就行了么,但现实是很多的应用之前都是建立在commons-logging+log4j的日志方式上的,有什么办法不改动应用的代码,达到commons-logging日志转到slf4j的目的么?把commons-logging.jar替换掉就好了,看下图:

    具体的原理是什么呢? 以LogFactory.getLog("loggerName")为例:

    1、org.apache.commons.logging.LogFactory类被jcl-over-slf4j包里面的同包同名类替换掉了。

    2、获取到的日志工厂是一个SLF4jLogFactory ,这个日志工厂在获取org.apache.commons.logging.Log 实例的时候,先基于前面描述的slf4j静态绑定机制,拿到了一个org.slf4j.Logger,然后用一个适配器类做接口转换,把slf4j的日志转换成commons-logging的日志器。 

    总结下来,我们有以下几点启发:

    1、适配器模式可以帮助我们做各种接口包的无缝集成。

    2、复杂的java应用还是在classloader机制上做文章,即使是slf4j的静态绑定机制 ,其实也是在编译时检查了classpath下是否有多个jar包包含StaticLoggerBinder类,

    真正运行的时候由线程上下文的classloader(默认是app classloader)来加载这个StaticLoggerBinder类而已。

    3、要分清Java日志系统里面各种包的作用: 

     1)日志接口包:commons-logging , slf4j-api 

     2) 日志框架包:log4j, logback, 

     3)  日志适配器包:slf4j-log4j,slf4j-jdk 

     4)  把某一个日志接口转换到另一个日志接口的桥接包:jcl-over-slf4j,log4j-over-slf4j 等。

  • 相关阅读:
    Leetcode 121. Best Time to Buy and Sell Stock
    Leetcode 120. Triangle
    Leetcode 26. Remove Duplicates from Sorted Array
    Leetcode 767. Reorganize String
    Leetcode 6. ZigZag Conversion
    KMP HDU 1686 Oulipo
    多重背包 HDU 2844 Coins
    Line belt 三分嵌套
    三分板子 zoj 3203
    二分板子 poj 3122 pie
  • 原文地址:https://www.cnblogs.com/dongqingswt/p/3605572.html
Copyright © 2011-2022 走看看