zoukankan      html  css  js  c++  java
  • maven 构建slf4j1.7.7之简单测试与源码解析

       参考:slf4j官网    http://www.slf4j.org/manual.html

    好像说一个东西 都有描述的 额 

    The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks, such as java.util.logging, logback and log4j. SLF4J allows the end-user to plug in the desired logging framework at deployment time. Note that SLF4J-enabling your library/application implies the addition of only a single mandatory dependency, namely slf4j-api-1.7.7.jar.


    我英文也不好啊  估摸着  大致意思是 SLF4J类似于JDBC一样 是一个规范或者说是日志的抽象层 在底层可以使用常见的日志框架进行实现,如log4j、commons logging等

    示意图




    示意图就比较形象了 


      1、使用myeclipse 2013 构建maven工程,名为slf4j     此处有类似构建过程  http://blog.csdn.net/undergrowth/article/details/25241213

      修改 添加slf4j-log4j12的依赖  pom.xml 如下  

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    
    	<groupId>com.undergrowth</groupId>
    	<artifactId>slf4j</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<packaging>jar</packaging>
    
    	<name>slf4j</name>
    	<url>http://maven.apache.org</url>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>junit</groupId>
    			<artifactId>junit</artifactId>
    			<version>3.8.1</version>
    			<scope>test</scope>
    		</dependency>
    		<!-- 因为maven会处理slf4j-log4j12的传递依赖于slf4j-api -->
    		<!-- <dependency> -->
    		<!-- <groupId>org.slf4j</groupId> -->
    		<!-- <artifactId>slf4j-api</artifactId> -->
    		<!-- <version>1.7.7</version> -->
    		<!-- </dependency> -->
    
    
    
    		<dependency>
    			<groupId>org.slf4j</groupId>
    			<artifactId>slf4j-log4j12</artifactId>
    			<version>1.7.7</version>
    		</dependency>
    
    		<!-- 如果配置多个日志框架 谁在前 用谁 -->
    		<!-- <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> 
    			<version>1.7.7</version> </dependency> -->
    
    	</dependencies>
    </project>
    


     2、修改测试类 App.java

    package com.undergrowth.slf4j;
    
    import java.util.Date;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * Hello world!
     *
     */
    public class App 
    {
    	final static Logger logger=LoggerFactory.getLogger(App.class);
    	
        public static void main( String[] args )
        {
           //不带占位符
        	logger.debug("hello world");
        	//带占位符
        	logger.debug("hello world {} ,{}", "under", new Date());
        	
        }
    }
    

    修改测试类 AppTest.java

    package com.undergrowth.slf4j;
    
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    
    /**
     * Unit test for simple App.
     */
    public class AppTest 
        extends TestCase
    {
        /**
         * Create the test case
         *
         * @param testName name of the test case
         */
        public AppTest( String testName )
        {
            super( testName );
        }
    
        /**
         * @return the suite of tests being tested
         */
        public static Test suite()
        {
            return new TestSuite( AppTest.class );
        }
    
        /**
         * Rigourous Test :-)
         */
        public void testApp()
        {
        	String[] testArgs={};
            App.main(testArgs);
        }
    }
    

    测试testApp方法  运行结果

    2014-九月-07 17:14:10 [main] DEBUG:  com.undergrowth.slf4j.App - hello world
    2014-九月-07 17:14:10 [main] DEBUG:  com.undergrowth.slf4j.App - hello world under ,Sun Sep 07 17:14:10 CST 2014
    


    附 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/">
    
    	<!-- Appenders -->
    	<appender name="console" class="org.apache.log4j.ConsoleAppender">
    		<param name="Target" value="System.out" />
    		<layout class="org.apache.log4j.PatternLayout">
    			<param name="ConversionPattern" value="%d{yyyy-MMM-dd HH:mm:ss} [%t] %-5p:  %c - %m%n" />
    		</layout>
    	</appender>
    	
    	<!-- Root Logger -->
    	<root>
    		<priority value="debug" />
    		<appender-ref ref="console" />
    	</root>
    	
    </log4j:configuration>
    

    以上即是slf4j的的简单实例

    3、查看slf4j的源码 简单解析其原理

       a:去github上clone 一份slf4j的源码  如下  

    git clone https://github.com/qos-ch/slf4j.git
      b:新建工程,导入myeclipse ,正常导入后 如下

        


         可能问到的问题 参考 

       解决Maven报Plugin execution not covered by lifecycle configuration   http://blog.csdn.net/xxd851116/article/details/25197373

      c: 在上面的实例中 使用slf4j首先使用的是 org.slf4j.LoggerFactory.getLogger方法获取日志对象 查看getLogger()

       

    /**
       * Return a logger named corresponding to the class passed as parameter, using
       * the statically bound {@link ILoggerFactory} instance.
       *
       * @param clazz the returned logger will be named after clazz
       * @return logger
       */
      public static Logger getLogger(Class clazz) {
        return getLogger(clazz.getName());
      }

      /**
       * Return a logger named according to the name parameter using the statically
       * bound {@link ILoggerFactory} instance.
       *
       * @param name The name of the logger.
       * @return logger
       */
      public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
      }
    

            看到调用了 getILoggerFactory()方法    查看

    /**
       * Return the {@link ILoggerFactory} instance in use.
       * <p/>
       * <p/>
       * ILoggerFactory instance is bound with this class at compile time.
       *
       * @return the ILoggerFactory instance in use
       */
      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.
            // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
            return TEMP_FACTORY;
        }
        throw new IllegalStateException("Unreachable code");
      }

    在该方法的注释中  很明确的有一行    我英文也不是很好  猜测 大致意思就是ILoggerFactory的实例对象绑定是在编译的时候决定的  这么绕呢

    ILoggerFactory instance is bound with this class at compile time.

    那就是说在编译的时候 ILoggerFactory这个接口  就指向了实际上实现它的子类上  

        看看ILoggerFacroty吧

        

    /**
     * Copyright (c) 2004-2011 QOS.ch
     * All rights reserved.
     *
     * Permission is hereby granted, free  of charge, to any person obtaining
     * a  copy  of this  software  and  associated  documentation files  (the
     * "Software"), to  deal in  the Software without  restriction, including
     * without limitation  the rights to  use, copy, modify,  merge, publish,
     * distribute,  sublicense, and/or sell  copies of  the Software,  and to
     * permit persons to whom the Software  is furnished to do so, subject to
     * the following conditions:
     *
     * The  above  copyright  notice  and  this permission  notice  shall  be
     * included in all copies or substantial portions of the Software.
     *
     * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
     * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
     * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     */
    package org.slf4j;
    
    
    /**
     * <code>ILoggerFactory</code> instances manufacture {@link Logger}
     * instances by name.
     * 
     * <p>Most users retrieve {@link Logger} instances through the static
     * {@link LoggerFactory#getLogger(String)} method. An instance of of this
     * interface is bound internally with {@link LoggerFactory} class at 
     * compile time. 
     * 
     * @author Ceki Gülcü
     */
    public interface ILoggerFactory {
      
      /**
       * Return an appropriate {@link Logger} instance as specified by the
       * <code>name</code> parameter.
       * 
       * <p>If the name parameter is equal to {@link Logger#ROOT_LOGGER_NAME}, that is 
       * the string value "ROOT" (case insensitive), then the root logger of the 
       * underlying logging system is returned.
       * 
       * <p>Null-valued name arguments are considered invalid.
       *
       * <p>Certain extremely simple logging systems, e.g. NOP, may always
       * return the same logger instance regardless of the requested name.
       * 
       * @param name the name of the Logger to return
       * @return a Logger instance 
       */
      public Logger getLogger(String name);
    }
    

    恩 也就只有一个 getLogger方法 好吧  还是接着看上面的getILoggerFactory方法吧

      显示一个 if 判断

       

      if (INITIALIZATION_STATE == UNINITIALIZED) {
          INITIALIZATION_STATE = ONGOING_INITIALIZATION;
          performInitialization();
        }

         很明显 第一次的话  肯定会执行  performInitialization(); 方法 查看

    private final static void performInitialization() {
        bind();
        if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
          versionSanityCheck();
        }
      }

    恩 bind();   接着看  

     private final static void bind() {
        try {
          Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
          reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
          // the next line does the binding
          StaticLoggerBinder.getSingleton();
          INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
          reportActualBinding(staticLoggerBinderPathSet);
          fixSubstitutedLoggers();
        } catch (NoClassDefFoundError ncde) {
          String msg = ncde.getMessage();
          if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
            INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
            Util.report("Failed to load class "org.slf4j.impl.StaticLoggerBinder".");
            Util.report("Defaulting to no-operation (NOP) logger implementation");
            Util.report("See " + NO_STATICLOGGERBINDER_URL
                    + " for further details.");
          } else {
            failedBinding(ncde);
            throw ncde;
          }
        } catch (java.lang.NoSuchMethodError nsme) {
          String msg = nsme.getMessage();
          if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
            INITIALIZATION_STATE = FAILED_INITIALIZATION;
            Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
            Util.report("Your binding is version 1.5.5 or earlier.");
            Util.report("Upgrade your binding to version 1.6.x.");
          }
          throw nsme;
        } catch (Exception e) {
          failedBinding(e);
          throw new IllegalStateException("Unexpected initialization failure", e);
        }
      }

    看到这个方法这么多 就知道是重头戏了啊

    先是   findPossibleStaticLoggerBinderPathSet()  方法

    // We need to use the name of the StaticLoggerBinder class, but we can't reference
      // the class itself.
      private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
    
      private static Set<URL> findPossibleStaticLoggerBinderPathSet() {
        // use Set instead of list in order to deal with  bug #138
        // LinkedHashSet appropriate here because it preserves insertion order during iteration
        Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
        try {
          ClassLoader loggerFactoryClassLoader = LoggerFactory.class
                  .getClassLoader();
          Enumeration<URL> paths;
          if (loggerFactoryClassLoader == null) {
            paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
          } else {
            paths = loggerFactoryClassLoader
                    .getResources(STATIC_LOGGER_BINDER_PATH);
          }
          while (paths.hasMoreElements()) {
            URL path = (URL) paths.nextElement();
            staticLoggerBinderPathSet.add(path);
          }
        } catch (IOException ioe) {
          Util.report("Error getting resources from path", ioe);
        }
        return staticLoggerBinderPathSet;
      }


    额  这么多 大致意思就是说 通过类加载器 获取到 org/slf4j/impl/StaticLoggerBinder.class  这个类的资源定位符  

        这里有一点 特别强调 也是我找了好久才找到的  你看  

             


      看到上面两张图 你会发现 slf4j-api里面 是有StaticLoggerBinder.java类的   log4j-12里面也是有 StaticLoggerBinder.java类的 那么按照道理来说 即使我在使用slf4j的时候不指定任何的实现日志框架  

    findPossibleStaticLoggerBinderPathSet() 这个方法 是不是至少都有一个结果?

    答案是否  原因在这



    看到了吧  原来slf4j-api在打包成jar的时候 删除了 org.slf4j.impl下面的所有java包  这也是下面代码成立的基础  这里不搞清楚 下面代码没法看啊


      这里 我做过测试了  在上面的pom.xml 里面 我已经做了注释  有多个日志框架的时候 谁在前 用谁    (当然这里是 排除掉联合多个日志框架的情况)

    这个 reportMultipleBindingAmbiguity方法呢 当你写了多个日志框架的时候 比如 你写了 log4j jcl 如上面的pom.xml中一样 人家就会友善的提醒你 你写了多个了  

      /**
       * Prints a warning message on the console if multiple bindings were found on the class path.
       * No reporting is done otherwise.
       *
       */
      private static void reportMultipleBindingAmbiguity(Set<URL> staticLoggerBinderPathSet) {
        if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
          Util.report("Class path contains multiple SLF4J bindings.");
          Iterator<URL> iterator = staticLoggerBinderPathSet.iterator();
          while (iterator.hasNext()) {
            URL path = (URL) iterator.next();
            Util.report("Found binding in [" + path + "]");
          }
          Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
        }
      }

    最重要的来了

     // the next line does the binding
          StaticLoggerBinder.getSingleton();

    在上面已经说过了 因为slf4j-api中的StaticLoggerBinder已经被删除了  所以当你配置了日志实现框架的的时候 比如上面pom.xml 中的log4j12的时候 此时获取到的字节码 就是slf4j-log4j12中的StaticLoggerBinder的字节码  那么slf4j-log4j12里面的StaticLoggerBinder代码 如下‘

    /**
     * Copyright (c) 2004-2011 QOS.ch
     * All rights reserved.
     *
     * Permission is hereby granted, free  of charge, to any person obtaining
     * a  copy  of this  software  and  associated  documentation files  (the
     * "Software"), to  deal in  the Software without  restriction, including
     * without limitation  the rights to  use, copy, modify,  merge, publish,
     * distribute,  sublicense, and/or sell  copies of  the Software,  and to
     * permit persons to whom the Software  is furnished to do so, subject to
     * the following conditions:
     *
     * The  above  copyright  notice  and  this permission  notice  shall  be
     * included in all copies or substantial portions of the Software.
     *
     * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
     * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
     * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     */
    package org.slf4j.impl;
    
    import org.apache.log4j.Level;
    import org.slf4j.ILoggerFactory;
    import org.slf4j.LoggerFactory;
    import org.slf4j.helpers.Util;
    import org.slf4j.spi.LoggerFactoryBinder;
    
    /**
     * The binding of {@link LoggerFactory} class with an actual instance of
     * {@link ILoggerFactory} is performed using information returned by this class.
     * 
     * @author Ceki Gülcü
     */
    public class StaticLoggerBinder implements LoggerFactoryBinder {
    
      /**
       * The unique instance of this class.
       * 
       */
      private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    
      /**
       * Return the singleton of this class.
       * 
       * @return the StaticLoggerBinder singleton
       */
      public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
      }
    
      /**
       * Declare the version of the SLF4J API this implementation is compiled
       * against. The value of this field is usually modified with each release.
       */
      // to avoid constant folding by the compiler, this field must *not* be final
      public static String REQUESTED_API_VERSION = "1.6.99"; // !final
    
      private static final String loggerFactoryClassStr = Log4jLoggerFactory.class
          .getName();
    
      /**
       * The ILoggerFactory instance returned by the {@link #getLoggerFactory}
       * method should always be the same object
       */
      private final ILoggerFactory loggerFactory;
    
      private StaticLoggerBinder() {
        loggerFactory = new Log4jLoggerFactory();
        try {
          Level level = Level.TRACE;
        } catch (NoSuchFieldError nsfe) {
          Util
              .report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version");
        }
      }
    
      public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
      }
    
      public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
      }
    }
    


    可以很清楚的看到 当使用 StaticLoggerBinder.getSingleton(); 的时候   使用的是单例模式  构建了一个StaticLoggerBinder对象  在它的构造函数里面构造了

    Log4jLoggerFactory对象

    源码

    /**
     * Copyright (c) 2004-2011 QOS.ch
     * All rights reserved.
     *
     * Permission is hereby granted, free  of charge, to any person obtaining
     * a  copy  of this  software  and  associated  documentation files  (the
     * "Software"), to  deal in  the Software without  restriction, including
     * without limitation  the rights to  use, copy, modify,  merge, publish,
     * distribute,  sublicense, and/or sell  copies of  the Software,  and to
     * permit persons to whom the Software  is furnished to do so, subject to
     * the following conditions:
     *
     * The  above  copyright  notice  and  this permission  notice  shall  be
     * included in all copies or substantial portions of the Software.
     *
     * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
     * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
     * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     */
    package org.slf4j.impl;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    
    import org.apache.log4j.LogManager;
    import org.slf4j.ILoggerFactory;
    import org.slf4j.Logger;
    
    /**
     * Log4jLoggerFactory is an implementation of {@link ILoggerFactory} returning
     * the appropriate named {@link Log4jLoggerAdapter} instance.
     * 
     * @author Ceki Gülcü
     */
    public class Log4jLoggerFactory implements ILoggerFactory {
    
      // key: name (String), value: a Log4jLoggerAdapter;
      ConcurrentMap<String, Logger> loggerMap;
    
    
      public Log4jLoggerFactory() {
        loggerMap = new ConcurrentHashMap<String, Logger>();
      }
    
      /*
       * (non-Javadoc)
       * 
       * @see org.slf4j.ILoggerFactory#getLogger(java.lang.String)
       */
      public Logger getLogger(String name) {
        Logger slf4jLogger = loggerMap.get(name);
        if (slf4jLogger != null) {
          return slf4jLogger;
        } else {
          org.apache.log4j.Logger log4jLogger;
          if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
            log4jLogger = LogManager.getRootLogger();
          else
            log4jLogger = LogManager.getLogger(name);
    
          Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
          Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
          return oldInstance == null ? newInstance : oldInstance;
        }
      }
    }
    

    会看到  Log4jLoggerFactory 的构造函数 是构建了一个  // key: name (String), value: a Log4jLoggerAdapter;   存放适配器的哈希map


    好  现在看起来 发现  还是没有最终的串联起来 因为还有一步就成功了啊

        

    看上面 bind()方法里面  当

    StaticLoggerBinder.getSingleton();
          INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;

    执行完后  初始化状态改为成功初始化 后面的就不说了 等此方法执行完后  回到 performInitialization()  再回到 getILoggerFactory()  看到有一个switch语句 

    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.
            // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
            return TEMP_FACTORY;
        }

    因为在bind()中 初始化状态为成功初始化 那么此时 这里即 执行  

    return StaticLoggerBinder.getSingleton().getLoggerFactory();

    上面说了  
    StaticLoggerBinder.getSingleton()

    的字节码为 slf4j-log4j12里面的StaticLoggerBinder  那么  即会调用  

     public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
      }


    返回在构造函数中创建的 Log4jLoggerFactory对象 (此对象也实现了ILoggerFactory接口)


    待getILoggerFactory()  执行完 返回到 getLogger(String name)  执行  

     return iLoggerFactory.getLogger(name);

    哦 这里不就是执行  Log4jLoggerFactory对象 的getLogger方法么  是啊 

      /*
       * (non-Javadoc)
       * 
       * @see org.slf4j.ILoggerFactory#getLogger(java.lang.String)
       */
      public Logger getLogger(String name) {
        Logger slf4jLogger = loggerMap.get(name);
        if (slf4jLogger != null) {
          return slf4jLogger;
        } else {
          org.apache.log4j.Logger log4jLogger;
          if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
            log4jLogger = LogManager.getRootLogger();
          else
            log4jLogger = LogManager.getLogger(name);
    
          Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
          Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
          return oldInstance == null ? newInstance : oldInstance;
        }
      }

    这里就是从map中按照名字查找 是否有相应的日志适配器 有的话 返回 没有的话 添加到map中 并返回 

    接着 再返回到getLogger(Class clazz)   此时slf4j中才拿到了Logger对象 返回给

    final static Logger logger=LoggerFactory.getLogger(App.class);
    上层


    那么slf4j的日志是怎么输出来的呢  刚才只是slf4j拿到Logger  其实  实现就是在 上面的适配器中  

    Log4jLoggerAdapter

    /**
     * Copyright (c) 2004-2011 QOS.ch
     * All rights reserved.
     *
     * Permission is hereby granted, free  of charge, to any person obtaining
     * a  copy  of this  software  and  associated  documentation files  (the
     * "Software"), to  deal in  the Software without  restriction, including
     * without limitation  the rights to  use, copy, modify,  merge, publish,
     * distribute,  sublicense, and/or sell  copies of  the Software,  and to
     * permit persons to whom the Software  is furnished to do so, subject to
     * the following conditions:
     *
     * The  above  copyright  notice  and  this permission  notice  shall  be
     * included in all copies or substantial portions of the Software.
     *
     * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
     * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
     * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     */
    package org.slf4j.impl;
    
    import java.io.Serializable;
    
    import org.apache.log4j.Level;
    import org.slf4j.Logger;
    import org.slf4j.Marker;
    import org.slf4j.helpers.FormattingTuple;
    import org.slf4j.helpers.MarkerIgnoringBase;
    import org.slf4j.helpers.MessageFormatter;
    import org.slf4j.spi.LocationAwareLogger;
    
    /**
     * A wrapper over {@link org.apache.log4j.Logger org.apache.log4j.Logger} in
     * conforming to the {@link Logger} interface.
     * 
     * <p>
     * Note that the logging levels mentioned in this class refer to those defined
     * in the <a
     * href="http://logging.apache.org/log4j/docs/api/org/apache/log4j/Level.html">
     * <code>org.apache.log4j.Level</code></a> class.
     * 
     * <p>
     * The TRACE level was introduced in log4j version 1.2.12. In order to avoid
     * crashing the host application, in the case the log4j version in use predates
     * 1.2.12, the TRACE level will be mapped as DEBUG. See also <a
     * href="http://bugzilla.slf4j.org/show_bug.cgi?id=68">bug 68</a>.
     * 
     * @author Ceki Gülcü
     */
    public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements
        LocationAwareLogger, Serializable {
    
      private static final long serialVersionUID = 6182834493563598289L;
    
      final transient org.apache.log4j.Logger logger;
    
      /**
       * Following the pattern discussed in pages 162 through 168 of "The complete
       * log4j manual".
       */
      final static String FQCN = Log4jLoggerAdapter.class.getName();
    
      // Does the log4j version in use recognize the TRACE level?
      // The trace level was introduced in log4j 1.2.12.
      final boolean traceCapable;
    
      // WARN: Log4jLoggerAdapter constructor should have only package access so
      // that
      // only Log4jLoggerFactory be able to create one.
      Log4jLoggerAdapter(org.apache.log4j.Logger logger) {
        this.logger = logger;
        this.name = logger.getName();
        traceCapable = isTraceCapable();
      }
    
      private boolean isTraceCapable() {
        try {
          logger.isTraceEnabled();
          return true;
        } catch (NoSuchMethodError e) {
          return false;
        }
      }
    
      /**
       * Is this logger instance enabled for the TRACE level?
       * 
       * @return True if this Logger is enabled for level TRACE, false otherwise.
       */
      public boolean isTraceEnabled() {
        if (traceCapable) {
          return logger.isTraceEnabled();
        } else {
          return logger.isDebugEnabled();
        }
      }
    
      /**
       * Log a message object at level TRACE.
       * 
       * @param msg
       *          - the message object to be logged
       */
      public void trace(String msg) {
        logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, msg, null);
      }
    
      /**
       * Log a message at level TRACE according to the specified format and
       * argument.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for level TRACE.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg
       *          the argument
       */
      public void trace(String format, Object arg) {
        if (isTraceEnabled()) {
          FormattingTuple ft = MessageFormatter.format(format, arg);
          logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, ft
              .getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level TRACE according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the TRACE level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg1
       *          the first argument
       * @param arg2
       *          the second argument
       */
      public void trace(String format, Object arg1, Object arg2) {
        if (isTraceEnabled()) {
          FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
          logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, ft
              .getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level TRACE according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the TRACE level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arguments
       *          an array of arguments
       */
      public void trace(String format, Object... arguments) {
        if (isTraceEnabled()) {
          FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
          logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, ft
              .getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log an exception (throwable) at level TRACE with an accompanying message.
       * 
       * @param msg
       *          the message accompanying the exception
       * @param t
       *          the exception (throwable) to log
       */
      public void trace(String msg, Throwable t) {
        logger.log(FQCN, traceCapable ? Level.TRACE : Level.DEBUG, msg, t);
      }
    
      /**
       * Is this logger instance enabled for the DEBUG level?
       * 
       * @return True if this Logger is enabled for level DEBUG, false otherwise.
       */
      public boolean isDebugEnabled() {
        return logger.isDebugEnabled();
      }
    
      /**
       * Log a message object at level DEBUG.
       * 
       * @param msg
       *          - the message object to be logged
       */
      public void debug(String msg) {
        logger.log(FQCN, Level.DEBUG, msg, null);
      }
    
      /**
       * Log a message at level DEBUG according to the specified format and
       * argument.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for level DEBUG.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg
       *          the argument
       */
      public void debug(String format, Object arg) {
        if (logger.isDebugEnabled()) {
          FormattingTuple ft = MessageFormatter.format(format, arg);
          logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level DEBUG according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the DEBUG level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg1
       *          the first argument
       * @param arg2
       *          the second argument
       */
      public void debug(String format, Object arg1, Object arg2) {
        if (logger.isDebugEnabled()) {
          FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
          logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level DEBUG according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the DEBUG level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arguments an array of arguments
       */
      public void debug(String format, Object... arguments) {
        if (logger.isDebugEnabled()) {
          FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments);
          logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log an exception (throwable) at level DEBUG with an accompanying message.
       * 
       * @param msg
       *          the message accompanying the exception
       * @param t
       *          the exception (throwable) to log
       */
      public void debug(String msg, Throwable t) {
        logger.log(FQCN, Level.DEBUG, msg, t);
      }
    
      /**
       * Is this logger instance enabled for the INFO level?
       * 
       * @return True if this Logger is enabled for the INFO level, false otherwise.
       */
      public boolean isInfoEnabled() {
        return logger.isInfoEnabled();
      }
    
      /**
       * Log a message object at the INFO level.
       * 
       * @param msg
       *          - the message object to be logged
       */
      public void info(String msg) {
        logger.log(FQCN, Level.INFO, msg, null);
      }
    
      /**
       * Log a message at level INFO according to the specified format and argument.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the INFO level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg
       *          the argument
       */
      public void info(String format, Object arg) {
        if (logger.isInfoEnabled()) {
          FormattingTuple ft = MessageFormatter.format(format, arg);
          logger.log(FQCN, Level.INFO, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at the INFO level according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the INFO level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg1
       *          the first argument
       * @param arg2
       *          the second argument
       */
      public void info(String format, Object arg1, Object arg2) {
        if (logger.isInfoEnabled()) {
          FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
          logger.log(FQCN, Level.INFO, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level INFO according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the INFO level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param argArray
       *          an array of arguments
       */
      public void info(String format, Object... argArray) {
        if (logger.isInfoEnabled()) {
          FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
          logger.log(FQCN, Level.INFO, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log an exception (throwable) at the INFO level with an accompanying
       * message.
       * 
       * @param msg
       *          the message accompanying the exception
       * @param t
       *          the exception (throwable) to log
       */
      public void info(String msg, Throwable t) {
        logger.log(FQCN, Level.INFO, msg, t);
      }
    
      /**
       * Is this logger instance enabled for the WARN level?
       * 
       * @return True if this Logger is enabled for the WARN level, false otherwise.
       */
      public boolean isWarnEnabled() {
        return logger.isEnabledFor(Level.WARN);
      }
    
      /**
       * Log a message object at the WARN level.
       * 
       * @param msg
       *          - the message object to be logged
       */
      public void warn(String msg) {
        logger.log(FQCN, Level.WARN, msg, null);
      }
    
      /**
       * Log a message at the WARN level according to the specified format and
       * argument.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the WARN level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg
       *          the argument
       */
      public void warn(String format, Object arg) {
        if (logger.isEnabledFor(Level.WARN)) {
          FormattingTuple ft = MessageFormatter.format(format, arg);
          logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at the WARN level according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the WARN level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg1
       *          the first argument
       * @param arg2
       *          the second argument
       */
      public void warn(String format, Object arg1, Object arg2) {
        if (logger.isEnabledFor(Level.WARN)) {
          FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
          logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level WARN according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the WARN level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param argArray
       *          an array of arguments
       */
      public void warn(String format, Object... argArray) {
        if (logger.isEnabledFor(Level.WARN)) {
          FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
          logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log an exception (throwable) at the WARN level with an accompanying
       * message.
       * 
       * @param msg
       *          the message accompanying the exception
       * @param t
       *          the exception (throwable) to log
       */
      public void warn(String msg, Throwable t) {
        logger.log(FQCN, Level.WARN, msg, t);
      }
    
      /**
       * Is this logger instance enabled for level ERROR?
       * 
       * @return True if this Logger is enabled for level ERROR, false otherwise.
       */
      public boolean isErrorEnabled() {
        return logger.isEnabledFor(Level.ERROR);
      }
    
      /**
       * Log a message object at the ERROR level.
       * 
       * @param msg
       *          - the message object to be logged
       */
      public void error(String msg) {
        logger.log(FQCN, Level.ERROR, msg, null);
      }
    
      /**
       * Log a message at the ERROR level according to the specified format and
       * argument.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the ERROR level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg
       *          the argument
       */
      public void error(String format, Object arg) {
        if (logger.isEnabledFor(Level.ERROR)) {
          FormattingTuple ft = MessageFormatter.format(format, arg);
          logger.log(FQCN, Level.ERROR, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at the ERROR level according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the ERROR level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param arg1
       *          the first argument
       * @param arg2
       *          the second argument
       */
      public void error(String format, Object arg1, Object arg2) {
        if (logger.isEnabledFor(Level.ERROR)) {
          FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
          logger.log(FQCN, Level.ERROR, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log a message at level ERROR according to the specified format and
       * arguments.
       * 
       * <p>
       * This form avoids superfluous object creation when the logger is disabled
       * for the ERROR level.
       * </p>
       * 
       * @param format
       *          the format string
       * @param argArray
       *          an array of arguments
       */
      public void error(String format, Object... argArray) {
        if (logger.isEnabledFor(Level.ERROR)) {
          FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
          logger.log(FQCN, Level.ERROR, ft.getMessage(), ft.getThrowable());
        }
      }
    
      /**
       * Log an exception (throwable) at the ERROR level with an accompanying
       * message.
       * 
       * @param msg
       *          the message accompanying the exception
       * @param t
       *          the exception (throwable) to log
       */
      public void error(String msg, Throwable t) {
        logger.log(FQCN, Level.ERROR, msg, t);
      }
    
      public void log(Marker marker, String callerFQCN, int level, String msg,
          Object[] argArray, Throwable t) {
        Level log4jLevel;
        switch (level) {
        case LocationAwareLogger.TRACE_INT:
          log4jLevel = traceCapable ? Level.TRACE : Level.DEBUG;
          break;
        case LocationAwareLogger.DEBUG_INT:
          log4jLevel = Level.DEBUG;
          break;
        case LocationAwareLogger.INFO_INT:
          log4jLevel = Level.INFO;
          break;
        case LocationAwareLogger.WARN_INT:
          log4jLevel = Level.WARN;
          break;
        case LocationAwareLogger.ERROR_INT:
          log4jLevel = Level.ERROR;
          break;
        default:
          throw new IllegalStateException("Level number " + level
              + " is not recognized.");
        }
        logger.log(callerFQCN, log4jLevel, msg, t);
      }
    
    }
    


    你会发现   在Logger中中有的方法 在这里一一都有对应   

     最终的输出 是通过 

    org.apache.log4j.Logger
    对于log4j12是这样的   jcl logging logback 原理 也应该都一样 


    卡  终于写完了   。。。



  • 相关阅读:
    数据库连接单例模式
    魔术方法
    序列化与反序列化
    设计模式
    类的自动加载
    错误处理
    匿名类--php7.0以上
    OpenCV中HSV颜色模型及颜色分量范围
    Opencv 轮廓提取
    opencv 二值化_OpenCV二值图像案例分析精选 | 第二期
  • 原文地址:https://www.cnblogs.com/liangxinzhi/p/4275563.html
Copyright © 2011-2022 走看看