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 原理 也应该都一样 


卡  终于写完了   。。。



原文地址:https://www.cnblogs.com/liangxinzhi/p/4275563.html