您的位置:首页 > 其它

maven 构建slf4j1.7.7之简单测试与源码解析

2014-09-07 18:41 405 查看
参考: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 此处有类似构建过程 /article/11572640.html

修改 添加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[/code]  b:新建工程,导入myeclipse ,正常导入后 如下



可能问到的问题 参考

解决Maven报Plugin
execution not covered by lifecycle configuration /article/8509387.html

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

卡 终于写完了 。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: