log4j 源码解析_slf4j + log4j原理实现及源码分析

本文介绍了Slf4j的基本概念和作用,它作为日志门面提供统一的日志接口,允许用户在运行时选择具体的日志实现。详细解释了Slf4j的初始化过程,包括如何通过StaticLoggerBinder找到并绑定日志实现。同时,对Log4j进行了简要介绍,并展示了Slf4j与Log4j的集成方式,包括Maven依赖、配置文件和使用示例。文章还深入解析了Slf4j与Log4j的源码,包括Slf4j的初始化、Log4j的初始化和日志输出流程。
摘要由CSDN通过智能技术生成

#0 系列目录#

#1 Slf4j#

##1.1 介绍##

SLF4J,即简单日志门面(Simple Logging Facade for Java)。从设计模式的角度考虑,它是用来在log和代码层之间起到门面的作用。对用户来说只要使用slf4j提供的接口,即可隐藏日志的具体实现。这与jdbc和相似。使用jdbc也就避免了不同的具体数据库。使用了slf4j可以对客户端应用解耦。因为当我们在代码实现中引入log日志的时候,用的是接口,所以可以实时的更具情况来调换具体的日志实现类。这就是slf4j的作用。

SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。

配置SLF4J是非常简单的一件事,只要将与你打算使用的日志系统对应的jar包加入到项目中,SLF4J就会自动选择使用你加入的日志系统。

##1.2 简单使用##

/**

* Slf4j 日志门面接口 Test

* @author taomk

* @version 1.0

* @since 15-10-15 下午3:39

*/

public class Slf4jFacadeTest {

private static Logger logger = LoggerFactory.getLogger(Slf4jFacadeTest.class);

public static void main(String[] args){

if(logger.isDebugEnabled()){

logger.debug("slf4j-log4j debug message");

}

if(logger.isInfoEnabled()){

logger.debug("slf4j-log4j info message");

}

if(logger.isTraceEnabled()){

logger.debug("slf4j-log4j trace message");

}

}

}

上述Logger接口、LoggerFactory类都是slf4j自己定义的。那么,SLF4J是怎么实现日志绑定的?

##1.3 日志绑定##

在应用中,通过LoggerFactory类的静态getLogger()获取logger,代码如下:

private static Logger logger = LoggerFactory.getLogger(Slf4jFacadeTest.class);

LoggerFactory.getLogger(Slf4jFacadeTest.class)方法的源码如下:LoggerFactory.java

public static Logger getLogger(String name) {

ILoggerFactory iLoggerFactory = getILoggerFactory();

return iLoggerFactory.getLogger(name);

}

上述获取Log的过程大致分成2个阶段:

获取ILoggerFactory的过程 (从字面上理解就是生产Logger的工厂);

根据ILoggerFactory获取Logger的过程;

下面来详细说明:

1 获取ILoggerFactory的过程,又可以分成3个过程:

1.1 从类路径中寻找org/slf4j/impl/StaticLoggerBinder.class类:LoggerFactory.java

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

private static Set findPossibleStaticLoggerBinderPathSet() {

// use Set instead of list in order to deal with bug #138

// LinkedHashSet appropriate here because it preserves insertion order during iteration

Set staticLoggerBinderPathSet = new LinkedHashSet();

try {

ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();

Enumeration 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;

}

如果找到多个,则输出 Class path contains multiple SLF4J bindings,表示有多个日志实现与slf4j进行了绑定。下面看下当出现多个StaticLoggerBinder的时候的输出日志(简化了一些内容):LoggerFactory.java

SLF4J: Class path contains multiple SLF4J bindings.

SLF4J: Found binding in [slf4j-log4j12-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: Found binding in [logback-classic-1.1.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: Found binding in [slf4j-jdk14-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

1.2 “随机选取"一个StaticLoggerBinder.class来创建一个单例:

private final static void bind() {

try {

Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();

// 打印搜索到的所有StaticLoggerBinder日志

reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);

// the next line does the binding 随机选取绑定

StaticLoggerBinder.getSingleton();

INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;

// 打印最终实际绑定StaticLoggerBinder日志

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);

}

}

1.3 根据上述创建的StaticLoggerBinder单例,返回一个ILoggerFactory实例:LoggerFactory.java

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");

}

所以slf4j与其他实际的日志框架的集成jar包中,都会含有这样的一个org/slf4j/impl/StaticLoggerBinder.class类文件,并且提供一个ILoggerFactory的实现。

2 根据ILoggerFactory获取Logger的过程:这就要看具体的ILoggerFactory类型了,下面与Log4j集成来详细说明。

#2 Log4j介绍#

Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;用户也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,用户能够更加细致地控制日志的生成过程。这些可以通过一个配置文件来灵活地进行配置,而不需要修改程序代码。具体详细介绍,参见Log4j架构分析与实战。

#3 Slf4j与Log4j集成#

##3.1 Maven依赖##

org.slf4j

slf4j-api

1.7.12

org.slf4j

slf4j-log4j12

1.7.12

log4j

log4j

1.2.17

##3.2 使用案例##

第一步:编写log4j.properties配置文件,放到类路径下

log4j.rootLogger = debug, console

log4j.appender.console = org.apache.log4j.ConsoleAppender

log4j.appender.console.layout = org.apache.log4j.PatternLayout

log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %m%n

配置文件的详细内容参见Log4J配置文件详解。

第二步:代码中如下使用

private static Logger logger=LoggerFactory.getLogger(Log4jSlf4JTest.class);

public static void main(String[] args){

if(logger.isDebugEnabled()){

logger.debug("slf4j-log4j debug message");

}

if(logger.isInfoEnabled()){

logger.info("slf4j-log4j info message");

}

if(logger.isTraceEnabled()){

logger.trace("slf4j-log4j trace message");

}

}

补充说明:

1 配置文件同样可以随意放置,如log4j1原生方式加载配置文件的方式log4j1原生开发

2 注意两者方式的不同:

slf4j: Logger logger=LoggerFactory.getLogger(Log4jSlf4JTest.class);

log4j: Logger logger=Logger.getLogger(Log4jTest.class);

slf4j的Logger是slf4j定义的接口,而log4j的Logger是类。LoggerFactory是slf4j自己的类。

##3.3 原理分析##

先来看下slf4j-log4j12包中的内容:

的确是有org/slf4j/impl/StaticLoggerBinder.class类。

该StaticLoggerBinder返回的ILoggerFactory类型将会是Log4jLoggerFactory。

Log4jLoggerAdapter就是实现了slf4j定义的Logger接口。

来看下具体过程:

1 获取对应的ILoggerFactory:从上面的slf4j的原理中我们知道:ILoggerFactory是由StaticLoggerBinder来创建出来的,所以可以简单分成2个过程:

1.1 第一个过程:slf4j寻找绑定类StaticLoggerBinder:使用ClassLoader来加载“org/slf4j/impl/StaticLoggerBinder.class"这样的类的url,然后就找到了slf4j-log4j12包中的StaticLoggerBinder。

1.2 第二个过程:创建出StaticLoggerBinder实例,并创建出ILoggerFactory,源码如下:

StaticLoggerBinder.getSingleton().getLoggerFactory();

以slf4j-log4j12中的StaticLoggerBinder为例,创建出的ILoggerFactory为Log4jLoggerFactory。

2 根据ILoggerFactory获取Logger的过程,来看下Log4jLoggerFactory是如何返回一个slf4j定义的Logger接口的实例的,源码如下:

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);

2.1 我们可以看到是通过log4j1的原生方式,即使用log4j1的LogManager来获取,引发log4j1的加载配置文件,然后初始化,最后返回一个org.apache.log4j.Logger log4jLogger,参见log4j1原生开发。

2.2 将上述的org.apache.log4j.Logger log4jLogger封装成Log4jLoggerAdapter,而Log4jLoggerAdapter是实现了slf4j的接口,所以我们使用的slf4j的Logger接口实例(这里即Log4jLoggerAdapter)都会委托给内部的org.apache.log4j.Logger实例。

#4 Slf4j与Log4j源码分析#

##4.1 Slf4j初始化##

初始化时机:下面这行代码,会在当前类类加载时或者显示得执行调用LoggerFactory.getLogger()方法时,触发Slf4j初始化,并绑定具体日志:Slf4jFacadeTest.java

private static Logger logger = LoggerFactory.getLogger(Slf4jFacadeTest.class);

初始化步骤:

源码1:LoggerFactory.java

public static Logger getLogger(String name) {

// 1. 初始化LoggerFactory,绑定具体日志,获得具体日志的LoggerFactory。

ILoggerFactory iLoggerFactory = getILoggerFactory();

// 2. 根据具体日志的LoggerFactory,触发具体日志的初始化并获得具体日志的Logger对象;

return iLoggerFactory.getLogger(name);

}

源码2:LoggerFactory.java

public static ILoggerFactory getILoggerFactory() {

// 1. 是否已经初始化了,否则进行初始化

if (INITIALIZATION_STATE == UNINITIALIZED) {

INITIALIZATION_STATE = ONGOING_INITIALIZATION;

performInitialization();

}

switch (INITIALIZATION_STATE) {

case SUCCESSFUL_INITIALIZATION:

// 2. 成功初始化,则直接获得具体日志的LoggerFactory

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");

}

源码3:LoggerFactory.java

private final static void performInitialization() {

// 1. 绑定具体的日志实现

bind();

if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {

versionSanityCheck();

}

}

源码4:LoggerFactory.java

private final static void bind() {

try {

// 1. 扫描查找“org/slf4j/impl/StaticLoggerBinder.class”

Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();

// 2. 打印找到多个“org/slf4j/impl/StaticLoggerBinder.class”的日志

reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);

// the next line does the binding 随机选择绑定,类加载器随机选择。

StaticLoggerBinder.getSingleton();

INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;

// 3. 打印绑定具体org/slf4j/impl/StaticLoggerBinder.class的日志

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);

}

}

源码5:LoggerFactory.java

private static Set findPossibleStaticLoggerBinderPathSet() {

// use Set instead of list in order to deal with bug #138

// LinkedHashSet appropriate here because it preserves insertion order during iteration

Set staticLoggerBinderPathSet = new LinkedHashSet();

try {

ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();

Enumeration paths;

if (loggerFactoryClassLoader == null) {

// 1. 扫包查找“org/slf4j/impl/StaticLoggerBinder.class”

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;

}

源码6:slf4j-log4j-1.6.2.jar中StaticLoggerBinder.java

private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

public static final StaticLoggerBinder getSingleton() {

// 1. 返回StaticLoggerBinder实例

return SINGLETON;

}

private StaticLoggerBinder() {

// 2. StaticLoggerBinder初始化

loggerFactory = new Log4jLoggerFactory();

try {

Level level = Level.TRACE;

}cache (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");

}

}

以上就是Slf4j初始化过程的源代码,其初始化过程就是绑定具体日志实现;

##4.2 Log4j初始化##

这里只在源码层级做分析,不想看源码,可直接参考具体详细流程,请参见Log4j初始化分析。

初始化时机:前一小节Slf4j已提到,在iLoggerFactory.getLogger(name)时触发Log4j初始化。iLoggerFactory具体类型为:Log4jLoggerFactory。

初始化步骤:

源码1:Log4jLoggerFactory.java

public Logger getLogger(String name) {

Logger slf4jLogger = null;

// protect against concurrent access of loggerMap

synchronized (this) {

slf4jLogger = (Logger) loggerMap.get(name);

if (slf4jLogger == null) {

// 1. 获取Logej具体的Logger对象。

org.apache.log4j.Logger log4jLogger;

if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME)) {

log4jLogger = LogManager.getRootLogger();

} else {

log4jLogger = LogManager.getLogger(name);

}

slf4jLogger = new Log4jLoggerAdapter(log4jLogger);

loggerMap.put(name, slf4jLogger);

}

}

return slf4jLogger;

}

源码2:LogManager.java静态代码块初始化Log4j

static {

// By default we use a DefaultRepositorySelector which always returns 'h'.

// 1. 初始化Logger仓库,并添加一个RootLogger实例,默认日志级别为DEBUG。

Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));

repositorySelector = new DefaultRepositorySelector(h);

/** Search for the properties file log4j.properties in the CLASSPATH. */

String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY, null);

// 2. 检查系统属性log4j.defaultInitOverride,如果该属性被设置为false,则执行初始化;否则(只要不是false,无论是什么值,甚至没有值,都是否则),跳过初始化。

// if there is no default init override, then get the resource

// specified by the user or the default config file.

if(override == null || "false".equalsIgnoreCase(override)) {

String configurationOptionStr = OptionConverter.getSystemProperty(

DEFAULT_CONFIGURATION_KEY,

null);

String configuratorClassName = OptionConverter.getSystemProperty(

CONFIGURATOR_CLASS_KEY,

null);

URL url = null;

// 3. 把系统属性log4j.configuration的值赋给变量resource。如果该系统变量没有被定义,则把resource赋值为"log4j.properties"。注意:在apache的log4j文档中建议使用定义log4j.configuration系统属性的方法来设置默认的初始化文件是一个好方法。

// if the user has not specified the log4j.configuration

// property, we search first for the file "log4j.xml" and then

// "log4j.properties"

if(configurationOptionStr == null) {

url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);

if(url == null) {

url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);

}

} else {

try {

// 4. 试图把resource变量转化成为一个URL对象url。如果一般的转化方法行不通,就调用org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)方法来完成转化。

url = new URL(configurationOptionStr);

} catch (MalformedURLException ex) {

// so, resource is not a URL:

// attempt to get the resource from the class path

url = Loader.getResource(configurationOptionStr);

}

}

// If we have a non-null url, then delegate the rest of the

// configuration to the OptionConverter.selectAndConfigure

// method.

if(url != null) {

LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");

try {

// 5. 如果url以".xml"结尾,则调用方法DOMConfigurator.configure(url)来完成初始化;否则,则调用方法PropertyConfigurator.configure(url)来完成初始化。如果url指定的资源不能被获得,则跳出初始化过程。

OptionConverter.selectAndConfigure(url, configuratorClassName,

LogManager.getLoggerRepository());

} catch (NoClassDefFoundError e) {

LogLog.warn("Error during default initialization", e);

}

} else {

LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");

}

} else {

LogLog.debug("Default initialization of overridden by " +

DEFAULT_INIT_OVERRIDE_KEY + "property.");

}

}

源码3:OptionConverter.java

public static void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {

Configurator configurator = null;

String filename = url.getFile();

// 1. 如果url以".xml"结尾,则调用方法DOMConfigurator.configure(url)来完成初始化;否则,则调用方法PropertyConfigurator.configure(url)来完成初始化。如果url指定的资源不能被获得,则跳出初始化过程。

if(clazz == null && filename != null && filename.endsWith(".xml")) {

clazz = "org.apache.log4j.xml.DOMConfigurator";

}

if(clazz != null) {

LogLog.debug("Preferred configurator class: " + clazz);

configurator = (Configurator) instantiateByClassName(clazz,

Configurator.class,

null);

if(configurator == null) {

LogLog.error("Could not instantiate configurator ["+clazz+"].");

return;

}

} else {

configurator = new PropertyConfigurator();

}

configurator.doConfigure(url, hierarchy);

}

##4.3 Log4j输出日志##

这里只在源码层级做分析,不想看源码,可直接参考具体详细流程,请参见Log4j输出日志。

预处理: 当调用Log4j的方法(如:debug(String, Throwable)、info(String, Throwable))输出日志时,首先对日志信息进行预处理。

源码1:Category.java(Logger继承自Category.java)

public void info(Object message) {

// 1. 根据全局日志等级threshold进行判断,如果日志等级低于threshold,不输出日志。

if(repository.isDisabled(Level.INFO_INT))

return;

// 2. 根据当前logger配置的日志等级level进行判断,如果日志等级低于当前logger配置的日志等级,不输出日志。

if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))

// 3. 将日志信息封装成LoggingEvent对象。

forcedLog(FQCN, Level.INFO, message, null);

}

源码2:Category.java

protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) {

// 1. 将LoggingEvent对象分发给所有的Appender。

callAppenders(new LoggingEvent(fqcn, this, level, message, t));

}

public void callAppenders(LoggingEvent event) {

int writes = 0;

for(Category c = this; c != null; c=c.parent) {

// Protected against simultaneous call to addAppender, removeAppender,...

synchronized(c) {

if(c.aai != null) {

// 2. 将LoggingEvent对象分发给所有的Appender。

writes += c.aai.appendLoopOnAppenders(event);

}

if(!c.additive) {

break;

}

}

}

if(writes == 0) {

repository.emitNoAppenderWarning(this);

}

}

public int appendLoopOnAppenders(LoggingEvent event) {

int size = 0;

Appender appender;

if(appenderList != null) {

size = appenderList.size();

for(int i = 0; i < size; i++) {

appender = (Appender) appenderList.elementAt(i);

appender.doAppend(event);

}

}

return size;

}

输出日志前还有两道工序需要处理:Filter处理和日志信息格式化。

源码1:

public synchronized void doAppend(LoggingEvent event) {

if (closed) {

LogLog.error("Attempted to append to closed appender named ["

+ name + "].");

return;

}

if (!isAsSevereAsThreshold(event.getLevel())) {

return;

}

// Filter处理

Filter f = this.headFilter;

FILTER_LOOP: while (f != null) {

// 1. 有三种返回值 DENY、ACCEPT、NEUTRAL,DENY表示丢弃当前日志信息,ACCEPT表示输出当前日志信息,NEUTRAL表示继续下一个Filter。Filter只能在XML配置文件中使用,Properties文件中不支持。

switch (f.decide(event)) {

case Filter.DENY:

return;

case Filter.ACCEPT:

break FILTER_LOOP;

case Filter.NEUTRAL:

f = f.getNext();

}

}

this.append(event);

}

public void append(LoggingEvent event) {

// Reminder: the nesting of calls is:

//

// doAppend()

// - check threshold

// - filter

// - append();

// - checkEntryConditions();

// - subAppend();

if(!checkEntryConditions()) {

return;

}

subAppend(event);

}

protected void subAppend(LoggingEvent event) {

// 2. 日志信息格式化:对日志进行格式化处理。

this.qw.write(this.layout.format(event));

if (layout.ignoresThrowable()) {

String[] s = event.getThrowableStrRep();

if (s != null) {

int len = s.length;

for (int i = 0; i < len; i++) {

this.qw.write(s[i]);

this.qw.write(Layout.LINE_SEP);

}

}

}

if (shouldFlush(event)) {

// 3. 将日志信息输出至目的地(文件、数据库或网格)

this.qw.flush();

}

}

若想了解Log4j的日志异步输出实现,请参见Log4j性能优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值