Java日志框架

作为一个Java程序员,肯定离不开日志框架,现在最优秀的Java日志框架是Log4j2,没有之一。根据官方的测试表明,在多线程环境下,Log4j2的异步日志表现更加优秀。在异步日志中,Log4j2使用独立的线程去执行I/O操作,可以极大地提升应用程序的性能。

在官方的测试中,下图比较了SyncAsync AppendersLoggers all async三者的性能。其中Loggers all async表现最为出色,而且线程数越多,Loggers all async性能越好。

除了对Log4j2自身的不同模式做对比以外,官方还做了Log4j2/Log4j1/Logback的对比,如下图所示

其中,Loggers all async是基于LMAX Disruptor实现的。

使用Log4j2

需要哪些JAR

使用Log4j2最少需要两个JAR,分别是log4j-api-2.xlog4j-core-2.x,其它JAR包根据应用程序需要添加。

配置文件位置

默认的,Log4j2在classpath下寻找名为log4j2.xml的配置文件。也可以使用system property指定配置文件的全路径。-Dlog4j.configurationFile=path/to/log4j2.xml,在Java代码中指定路径如下所示


import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.core.LoggerContext;import java.io.File;public class Demo {public static void main(String[] args) {LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);File file = new File("path/to/a/different/log4j2.xml");loggerContext.setConfigLocation(file.toURI());}}
 

一般的,不需要手动关闭Log4j2,如果想手动在代码中关闭Log4j2如下所示

package com;import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configurator;public class Demo {public static void main(String[] args) {Configurator.shutdown((LoggerContext) LogManager.getContext());}
}
 

有关Log4j2的内容很多,不能一一列出,如果在开发中遇到任何问题,推荐去官方文档中寻找解决方案。

不同的线程输出日志到不同的文件中

方法一使用ThreadContext

在多线程编程中,如果不做特殊的设置,那么多个线程的日志会输出到同一个日志文件中,这样在查阅日志的时候,会带来诸多不便。很自然地,我们想到了让不同的线程输出日志到不同的文件中,这样不是更好吗?在翻阅官方文档过程中,找到了FAQ(Frequently Asked Questions),其中有个问题How do I dynamically write to separate log files?正是我们所需要的。根据提示步步推进可以顺利解决该问题。其中log4j2.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF"><Appenders><Routing name="Routing"><Routes pattern="$${ctx:ROUTINGKEY}"><!-- This route is chosen if ThreadContext has value 'special' for key ROUTINGKEY. --><Route key="special"><RollingFile name="Rolling-${ctx:ROUTINGKEY}"fileName="logs/special-${ctx:ROUTINGKEY}.log"filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-special-%d{yyyy-MM-dd}-%i.log.gz"><PatternLayout><Pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</Pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy interval="6"modulate="true" /><SizeBasedTriggeringPolicy size="10 MB" /></Policies></RollingFile></Route><!-- This route is chosen if ThreadContext has no value for key ROUTINGKEY. --><Route key="$${ctx:ROUTINGKEY}"><RollingFile name="Rolling-default"fileName="logs/default.log"filePattern="./logs/${date:yyyy-MM}/default-%d{yyyy-MM-dd}-%i.log.gz"><PatternLayout><pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy interval="6"modulate="true" /><SizeBasedTriggeringPolicy size="10 MB" /></Policies></RollingFile></Route><!-- This route is chosen if ThreadContext has a value for ROUTINGKEY (other than the value 'special' which had its own route above). The value dynamically determines the name of the log file. --><Route><RollingFile name="Rolling-${ctx:ROUTINGKEY}"fileName="logs/other-${ctx:ROUTINGKEY}.log"filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-other-%d{yyyy-MM-dd}-%i.log.gz"><PatternLayout><pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy interval="6"modulate="true" /><SizeBasedTriggeringPolicy size="10 MB" /></Policies></RollingFile></Route></Routes></Routing><!--很直白,Console指定了结果输出到控制台 --><Console name="ConsolePrint" target="SYSTEM_OUT"><PatternLayoutpattern="%d{yyyy.MM.dd HH:mm:ss z} %t %-5level %class{36} %L %M - %msg%xEx%n" /></Console></Appenders><Loggers><!-- 级别顺序(低到高):TRACE < DEBUG < INFO < WARN < ERROR < FATAL --><Root level="DEBUG" includeLocation="true"><!--AppenderRef中的ref值必须是在前面定义的appenders --><AppenderRef ref="Routing" /><AppenderRef ref="ConsolePrint" /></Root></Loggers>
</Configuration>

测试类如下所示

package com;import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.ThreadContext;@Log4j2
public class TestLog {public static void main(String[] args) {new Thread(() -> {ThreadContext.put("ROUTINGKEY", Thread.currentThread().getName());log.info("info");log.debug("debug");log.error("error");ThreadContext.remove("ROUTINGKEY");}).start();new Thread(() -> {ThreadContext.put("ROUTINGKEY", Thread.currentThread().getName());log.info("info");log.debug("debug");log.error("error");ThreadContext.remove("ROUTINGKEY");}).start();}
}
 

运行测试类,会得到如下两个日志文件,other-Thread-1.logother-Thread-2.log,每个日志文件对应着一个线程。该程序使用Gradle构建,依赖的JAR包如下:

1

2

3

4

5

dependencies {

compile 'org.projectlombok:lombok:1.16.10'

compile 'org.apache.logging.log4j:log4j-core:2.6'

compile 'org.apache.logging.log4j:log4j-api:2.6'

}

需要注意的一点是,每次在使用log对象之前,需要先设置ThreadContext.put("ROUTINGKEY", Thread.currentThread().getName());,设置的keylog4j2.xml配置文件中的key要一致,而value可以是任意值,参考配置文件即可理解。

有没有发现,每次使用log对象,还需要添加额外的代码,这不是恶心他妈给恶心开门——恶心到家了吗?有没有更加优雅地解决办法呢?且看下节。

方法二实现StrLookup

修改log4j2.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF"><Appenders><Routing name="Routing"><Routes pattern="$${thread:threadName}"><Route><RollingFile name="logFile-${thread:threadName}"fileName="logs/concurrent-${thread:threadName}.log"filePattern="logs/concurrent-${thread:threadName}-%d{MM-dd-yyyy}-%i.log"><PatternLayout pattern="%d %-5p [%t] %C{2} - %m%n"/><Policies><SizeBasedTriggeringPolicy size="50 MB"/></Policies><DefaultRolloverStrategy max="100"/></RollingFile></Route></Routes></Routing><Async name="async" bufferSize="1000" includeLocation="true"><AppenderRef ref="Routing"/></Async><!--很直白,Console指定了结果输出到控制台--><Console name="ConsolePrint" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %t %-5level %class{36} %L %M - %msg%xEx%n"/></Console></Appenders><Loggers><Root level="info" includeLocation="true"><AppenderRef ref="async"/><AppenderRef ref="ConsolePrint"/></Root></Loggers>
</Configuration>

实现StrLookup中的lookup方法,代码如下:

package com;import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;@Plugin(name = "thread", category = StrLookup.CATEGORY)
public class ThreadLookup implements StrLookup {@Overridepublic String lookup(String key) {return Thread.currentThread().getName();}@Overridepublic String lookup(LogEvent event, String key) {return event.getThreadName() == null ? Thread.currentThread().getName() : event.getThreadName();}}
 

编写测试类,代码如下:

package com;import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.ThreadContext;@Log4j2
public class TestLog {public static void main(String[] args) {new Thread(() -> {log.info("info");log.debug("debug");log.error("error");}).start();new Thread(() -> {log.info("info");log.debug("debug");log.error("error");}).start();}
}

该测试类同样会得到两个日志文件,每个线程分别对应一个,但是在使用log对象之前不再需要设置ThreadContext.put("ROUTINGKEY", Thread.currentThread().getName());,是不是清爽多了。

根据官方的性能测试我们知道,Loggers all async的性能最高,但是方法一使用的是Sync模式(因为Appender默认是synchronous的),方法二使用的是Async Appender模式,那么如何更进一步让所有的Loggers都是Asynchronous的,让我们的配置更完美呢?想要使用Loggers all async还需要做一步设置,如果是Maven或Gradle项目,需要在src/main/resources目录下添加log4j2.component.properties配置文件,根据官网说明,其中内容为

1

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

同时还需要在classpath下添加依赖的disruptor JAR包com.lmax:disruptor:3.3.6。当配置使用了AsyncLoggerContextSelector之后,所有的Loggers就都是异步的了。有方法证明使用了Loggers all async吗,答案是有,默认的location不会传递给Loggers all async的I/O线程,所以如果不设置includeLocation=true的话,打印出来的日志中location信息是“?”,例如“2016-12-16 16:38:47,285 INFO [Thread-3] ? - info”,如果设置includeLocation=”true”之后,打印出“2016-12-16 16:39:14,899 INFO [Thread-3] TestLog - info”,Gradle构建依赖如下:

1

2

3

4

5

6

dependencies {

compile 'org.projectlombok:lombok:1.16.10'

compile 'org.apache.logging.log4j:log4j-core:2.6'

compile 'org.apache.logging.log4j:log4j-api:2.6'

compile 'com.lmax:disruptor:3.3.6'

}

不同级别的日志输出到不同的文件中

通常情况下,用到的日志级别主要是info/debug/error三个,而如果不做特殊配置,这三者信息是写到一个日志文件中的,当我们需要不同级别的日志输出到不同的文件中时,需要如何做呢?log4j2.xml配置信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF"><Properties><Property name="logFilePath">logs</Property><Property name="logFileName">testLog</Property></Properties><Appenders><!--很直白,Console指定了结果输出到控制台--><Console name="ConsolePrint" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %t %-5level %class{36} %L %M - %msg%xEx%n"/></Console><!--<File>输出结果到指定文件</File>--><!--<RollingFile>同样输出结果到指定文件,但是使用buffer,速度会快点</RollingFile>--><!--filePattern:表示当日志到达指定的大小或者时间,产生新日志时,旧日志的命名路径。--><!--PatternLayout:和log4j一样,指定输出日志的格式,append表示是否追加内容,值默认为true--><RollingFile name="RollingFileDebug" fileName="${logFilePath}/${logFileName}-debug.log"filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}_%i.log.gz"><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><!--注意,如果有多个ThresholdFilter,那么Filters标签是必须的--><Filters><!--首先需要过滤不符合的日志级别,把不需要的首先DENY掉,然后在ACCEPT需要的日志级别,次序不能颠倒--><!--INFO及以上级别拒绝输出--><ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/><!--只输出DEBUG级别信息--><ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/></Filters><Policies><!--时间策略,每隔24小时产生新的日志文件--><TimeBasedTriggeringPolicy/><!--大小策略,每到30M时产生新的日志文件--><SizeBasedTriggeringPolicy size="30MB"/></Policies></RollingFile><RollingFile name="RollingFileInfo" fileName="${logFilePath}/${logFileName}-info.log"filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}_%i.log.gz"><Filters><!--onMatch:Action to take when the filter matches. The default value is NEUTRAL--><!--onMismatch:    Action to take when the filter does not match. The default value is DENY--><!--级别在ERROR之上的都拒绝输出--><!--在组合过滤器中,接受使用NEUTRAL(中立),被第一个过滤器接受的日志信息,会继续用后面的过滤器进行过滤,只有符合所有过滤器条件的日志信息,才会被最终写入日志文件--><ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/></Filters><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="30MB"/></Policies></RollingFile><RollingFile name="RollingFileError" fileName="${logFilePath}/${logFileName}-error.log"filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}_%i.log.gz"><Filters><ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/></Filters><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="30MB"/></Policies></RollingFile></Appenders><Loggers><!--logger用于定义log的level以及所采用的appender,如果无需自定义,可以使用root解决,root标签是log的默认输出形式--><!-- 级别顺序(低到高):TRACE < DEBUG < INFO < WARN < ERROR < FATAL --><Root level="DEBUG" includeLocation="true"><!-- 只要是级别比ERROR高的,包括ERROR就输出到控制台 --><!--appender-ref中的值必须是在前面定义的appenders--><Appender-ref level="ERROR" ref="ConsolePrint"/><Appender-ref ref="RollingFileDebug"/><Appender-ref ref="RollingFileInfo"/><Appender-ref ref="RollingFileError"/></Root></Loggers>
</Configuration>

src\main\resources\log4j2.component.properties内容不变,如下

1

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

测试代码如下

package com;import lombok.extern.log4j.Log4j2;@Log4j2
public class TestLog {public static void main(String[] args) {new Thread(() -> {log.info("info");log.debug("debug");log.error("error");}).start();new Thread(() -> {log.info("info");log.debug("debug");log.error("error");}).start();}
}
 

该程序会生成三份日志文件【testLog-debug.log,testLog-error.log,testLog-info.log】,如果你足够细心的话,就会发现线程1和线程2的info|debug|error信息都输出到一个info|debug|error日志文件中了。如何解决这个问题呢?换句话说,我想得到

Thread 1

Thread 1的info日志
Thread 1的debug日志
Thread 1的error日志

Thread 2

Thread 2的info日志
Thread 2的debug日志
Thread 2的error日志

六个日志文件,要如何实现呢?这正是下一节要讲述的内容。

不同线程不同级别的日志输出到不同的文件中

要实现该功能,还要从RoutingAppender身上做文章。RoutingAppender主要用来评估LogEvents,然后将它们路由到下级Appender。目标Appender可以是先前配置的并且可以由其名称引用的Appender,或者可以根据需要动态地创建Appender。RoutingAppender应该在其引用的任何Appenders之后配置,以确保它可以正确关闭。

RoutingAppender中的name属性用来指定该Appender的名字,它可以包含多个Routes子节点,用来标识选择Appender的条件,而Routes只有一个属性“pattern”,该pattern用来评估所有注册的Lookups,并且其结果用于选择路由。在Routes下可以有多个Route,每个Route都必须配置一个key,如果这个key匹配“pattern”的评估结果,那么这个Route就被选中。同时每个Route都必须引用一个Appender,如果这个Route包含一个ref属性,那么这个Route将引用一个在配置中定义的Appender,如果这个Route包含一个Appender的定义,那么这个Appender将会根据RoutingAppender的上下文创建并被重用。

废话说多了,直接上配置才简洁明了。log4j2.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--status的含义为是否记录log4j2本身的event信息,默认是OFF-->
<Configuration status="OFF"><Properties><!--自定义一些常量,之后使用${变量名}引用--><Property name="logFilePath">logs</Property><Property name="logFileName">testLog</Property></Properties><Appenders><!--很直白,Console指定了结果输出到控制台--><Console name="ConsolePrint" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %t %-5level %class{36} %L %M - %msg%xEx%n"/></Console><!--<File>输出结果到指定文件</File>--><!--<RollingFile>同样输出结果到指定文件,但是使用buffer,速度会快点</RollingFile>--><!--filePattern:表示当日志到达指定的大小或者时间,产生新日志时,旧日志的命名路径。--><!--PatternLayout:和log4j一样,指定输出日志的格式,append表示是否追加内容,值默认为true--><Routing name="RollingFileDebug_${thread:threadName}"><Routes pattern="$${thread:threadName}"><Route><RollingFile name="RollingFileDebug_${thread:threadName}"fileName="${logFilePath}/${logFileName}_${thread:threadName}_debug.log"filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}-${thread:threadName}-debug_%i.log.gz"><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><!--注意,如果有多个ThresholdFilter,那么Filters标签是必须的--><Filters><!--首先需要过滤不符合的日志级别,把不需要的首先DENY掉,然后在ACCEPT需要的日志级别,次序不能颠倒--><!--INFO及以上级别拒绝输出--><ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/><!--只输出DEBUG级别信息--><ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/></Filters><Policies><!--时间策略,每隔24小时产生新的日志文件--><TimeBasedTriggeringPolicy/><!--大小策略,每到30M时产生新的日志文件--><SizeBasedTriggeringPolicy size="30MB"/></Policies></RollingFile></Route></Routes></Routing><Routing name="RollingFileInfo_${thread:threadName}"><Routes pattern="$${thread:threadName}"><Route><RollingFile name="RollingFileInfo_${thread:threadName}"fileName="${logFilePath}/${logFileName}_${thread:threadName}_info.log"filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}-${thread:threadName}-info_%i.log.gz"><Filters><!--onMatch: Action to take when the filter matches. The default value is NEUTRAL--><!--onMismatch:    Action to take when the filter does not match. The default value is DENY--><!--级别在ERROR之上的都拒绝输出--><!--在组合过滤器中,接受使用NEUTRAL(中立),被第一个过滤器接受的日志信息,会继续用后面的过滤器进行过滤,只有符合所有过滤器条件的日志信息,才会被最终写入日志文件--><ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/></Filters><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="30MB"/></Policies></RollingFile></Route></Routes></Routing><Routing name="RollingFileError_${thread:threadName}"><Routes pattern="$${thread:threadName}"><Route><RollingFile name="RollingFileError_${thread:threadName}"fileName="${logFilePath}/${logFileName}_${thread:threadName}_error.log"filePattern="${logFilePath}/$${date:yyyy-MM}/${logFileName}-%d{yyyy-MM-dd}-${thread:threadName}-error_%i.log.gz"><Filters><ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/></Filters><PatternLayout pattern="%d{yyyy.MM.dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><Policies><TimeBasedTriggeringPolicy/><SizeBasedTriggeringPolicy size="30MB"/></Policies></RollingFile></Route></Routes></Routing><!--bufferSize整数,指定可以排队的events最大数量,如果使用BlockingQueue,这个数字必须是2的幂次--><!--includeLocation默认值是FALSE,如果指定为TRUE,会降低性能,但是推荐设置为TRUE,否则不打印位置行信息--><Async name="async" bufferSize="262144" includeLocation="true"><AppenderRef ref="RollingFileDebug_${thread:threadName}"/><AppenderRef ref="RollingFileInfo_${thread:threadName}"/><AppenderRef ref="RollingFileError_${thread:threadName}"/><!-- 只要是级别比ERROR高的,包括ERROR就输出到控制台 --><AppenderRef ref="ConsolePrint" level="ERROR"/></Async></Appenders><Loggers><!--Logger用于定义log的level以及所采用的appender,如果无需自定义,可以使用root解决,root标签是log的默认输出形式--><!-- 级别顺序(低到高):TRACE < DEBUG < INFO < WARN < ERROR < FATAL --><Root level="DEBUG" includeLocation="true"><!--AppenderRef中的ref值必须是在前面定义的appenders--><AppenderRef ref="async"/></Root></Loggers>
</Configuration>

log4j2.component.propertiesThreadLookup类不变,依赖的JAR包和上一节一样。测试类如下

package com;import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.ThreadContext;@Log4j2
public class TestLog {public static void main(String[] args) {new Thread(() -> {log.info("info");log.debug("debug");log.error("error");}).start();new Thread(() -> {log.info("info");log.debug("debug");log.error("error");}).start();}
}

该程序会输出六个日志文件,分别是

testLog_Thread-2_debug.log
testLog_Thread-2_error.log
testLog_Thread-2_info.log
testLog_Thread-3_debug.log
testLog_Thread-3_error.log
testLog_Thread-3_info.log

至此,就实现了不同线程不同级别的日志输出到不同文件中的功能。

如何启用All Loggers Asynchronous

为了使得所有的Loggers都是异步的,除了添加一个新的配置文件,就是log4j2.component.properties外,还有其它方式吗?有的,仅列举如下

  • 例如【IntelliJ IDEA】中使用Gradle构建项目,那么可以在Settings | Build, Execution, Deployment | Build Tools | Gradle | Gradle VM options中填入

    1

    -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

  • 另一种就是在前面提到的ThreadLookup类中,添加静态代码块

    1

    2

    3

    static {

    System.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");

    }

根据参考手册,有一点需要注意的就是,要使用<Root><Logger>标签,而不是<asyncRoot><asyncLogger>,原文如下:

When AsyncLoggerContextSelector is used to make all loggers asynchronous, make sure to use normal <root> and <logger> elements in the configuration. The AsyncLoggerContextSelector will ensure that all loggers are asynchronous, using a mechanism that is different from what happens when you configure <asyncRoot> or <asyncLogger>. The latter elements are intended for mixing async with sync loggers.

混合使用Synchronous和Asynchronous Loggers

需要disruptor-3.0.0.jar或更高版本的jar包,不需要设置系统属性Log4jContextSelector,在配置中可以混合使用Synchronousasynchronous loggers,使用<AsyncRoot>或者<AsyncLogger>去指定需要异步的Loggers,<AsyncLogger>元素还可以包含<Root><Logger>用于同步的Loggers。注意如果使用的是<AsyncRoot>或者<AsyncLogger>,那么就无需设置系统属性Log4jContextSelector了。

一个混合了同步和异步的Loggers配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- No need to set system property "Log4jContextSelector" to any valuewhen using <asyncLogger> or <asyncRoot>. -->
<Configuration status="WARN"><Appenders><!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. --><RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log"immediateFlush="false" append="false"><PatternLayout><Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern></PatternLayout></RandomAccessFile></Appenders><Loggers><!-- pattern layout actually uses location, so we need to include it --><AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true"><AppenderRef ref="RandomAccessFile"/></AsyncLogger><Root level="info" includeLocation="true"><AppenderRef ref="RandomAccessFile"/></Root></Loggers>
</Configuration>

springboot slf4j log4j2 动态创建日志的方法相关推荐

  1. SpringBoot框架,使用Log4j2+Lombok引入日志的方法

    日志分为不同的级别,我比较常用的就是:debug,info,error. 对于一个系统来说,日志是很重要的部分,日志可以帮助我们快速查找系统相关异常,可以帮助开发人员调试程序,可以监控系统程序运行情况 ...

  2. python 动态_python实现动态创建类的方法分析

    本文实例讲述了python实现动态创建类的方法.分享给大家供大家参考,具体如下: python作为动态语言,如何在运行时动态创建类呢(python Creating classes dynamical ...

  3. log4j2动态修改日志级别及拓展性使用

    一.供参考的完整日志配置 <?xml version="1.0" encoding="UTF-8"?><!-- 配置LoggerConfig, ...

  4. Python动态创建变量的方法

    Python内置函数exec()可以用来执行Python代码或内置函数compile()编译的代码对象,例如 >>> exec('print("hello world&qu ...

  5. SpringBoot根据配置文件动态创建Bean

    需求场景: 如果现在需要实现一个这样的需求: 根据配置信息动态控制是否创建任意Bean 通常我们会定义这样的配置: application.yml: 实现业务需求:根据enbaled控制下面bean- ...

  6. Springboot使用Log4j2的配置详解

    Springboot使用Log4j2的配置 日志框架的对比和选择 一.依赖配置(pom文件) 二.log4j2.xml文件的配置 三.Log4j2.xml配置文件核心概念解析 1.全局Property ...

  7. 不重启JVM动态添加日志(阿里Arthas)

    一.背景 如果生产环境临时出现故障,但是现运行代码未打印定位问题所需要的日志,我们通常的做法是添加日志->重新发布->重现故障.但是这样麻烦不说,最重要的是重启节点会丢失现场,也不一定能重 ...

  8. C语言-动态创建二维数组

    文章目录 1 前言 2 动态创建的实现方法 2.1 方法1(适用于二级指针传递) 2.2 方法2(模拟C语言数组的储存方式) 1 前言 参照我之前的一篇文章 二维数组做函数的参数 文章中提到,当我们想 ...

  9. silverlight动态创建WCF服务

    最近在发布silverlight项目时,服务地址不是固定,因此服务需要动态创建.在网上搜了点资料找到了动态创建服务的方法,直接上代码 /// <summary>/// 根据服务类型创建服务 ...

最新文章

  1. CStatic 控件设置文本,不能重回问题
  2. ORA-03001,GATHER_TABLE_STATS数据库自动收集统计信息报错
  3. python能开发手机程序吗_python能否开发安卓应用app?当然可以,python助你轻松搞定...
  4. 《Java 核心技术卷1 第10版》学习笔记------日志
  5. 一家永不上市,利润共享的行业龙头,依旧还是那个务实创新的少年
  6. java switch枚举类,使用枚举类enum代替switch
  7. linux监听报错sp2-0734,Linux中Oracle启动侦听报错TNS:permission denied的解决方法
  8. LINGO编程简介与实例
  9. AI智能电话机器人源码搭建原理及步骤
  10. 联想笔记本键盘亮屏幕不亮_联想笔记本电脑开机键亮但是黑屏?键盘没反应?
  11. nsis检测vc2013运行环境vcredist_x86.exe是否已经安装
  12. 深度学习中的优化算法之Adadelta
  13. 前端:分享几个前端素材网站
  14. i print打印监控系统
  15. 计算机考研数学2019,2019计算机考研数学复习:最常遇到的10个问题
  16. spring-ant-处理zip
  17. 从键盘上输入以下的数据:TOM:89|JERRY:90|TONY:95,数据格式为“姓名:成绩|姓名:成绩|姓名:成绩”,对输入的内容按成绩进行排序,并将结果按成绩由高到低排序。
  18. 算法进阶面试题07——求子数组的最大异或和(前缀树)、换钱的方法数(递归改dp最全套路解说)、纸牌博弈、机器人行走问题
  19. 我是云南的云南怒江的是什么梗 我是云南的云南怒江的梗出处
  20. 如何设置HttpClient请求的Content-Type标头?

热门文章

  1. Hive之DQL操作
  2. 运用JAVA基础阶段知识,模拟双色球机选抽奖过程
  3. 合理利用计算机虚拟内存,合理设置虚拟内存,目前8G内存足够日常使用
  4. 计算机专业学习阶段计划书怎么写,计算机专业学习计划优秀范文2篇
  5. inversion 矩阵_inversion是什么意思_inversion怎么读_inversion翻译_用法_发音_词组_同反义词_倒置-新东方在线英语词典...
  6. 摄影中的曝光补偿、白加黑减
  7. kaggle之识别谷歌街景图片中的字母
  8. windows设备管理器_如何打开Windows设备管理器?
  9. C语言 一球从100米高度自由落下,每次落地后反跳回原高度的一半; 再落下,求它在第10次落地时,共经过多少米?第10次反弹多高?
  10. 七年老安卓的九十月小结