????? ? 所以slffj需要依赖于log4j来实现,使用log4j2而不是log4j是因为Log4j 1.x 在高并发情况下出现死锁导致cpu使用率异常飙升,而Log4j2.0基于LMAX Disruptor的异步日志在多线程环境下性能会远远优于Log4j 1.x和logback(官方数据是10倍以上)。我们需要注意的是? log4j2 已经弃用了.properties 的配置格式。
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
or
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
解决了log4j到log4j2的问题,接下来我们需要引入slf4j,开始导包
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!--用于与slf4j保持桥接-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/classes/log4j2.xml</param-value>
</context-param>
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration monitorinterval="10">
<!--线上环境 cicd-->
<properties>
<property name="LOG_HOME">${sys:log.dir}</property>
<property name="projectName">testProject</property>
</properties>
<!--输出源-->
<Appenders>
<!--输出到控制台-->
<Console name="STDOUT" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss sss} %-5p %t %l(%r) %m%n" />
<ThresholdFilter level="info" />
</Console>
<!--输出到文件-->
<RollingFile name="RollingFileDebug" fileName="${LOG_HOME}/${projectName}/${projectName}.111.log"
filePattern="${LOG_HOME}/${projectName}/${projectName}.log.%d{yyyy-MM-dd HH}h">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss sss} %-5p %t %l %m%n " />
<ThresholdFilter level="DEBUG" />
<Policies>
<TimeBasedTriggeringPolicy interval="24" />
</Policies>
<!--按照时间间隔切割文件,切割间隔为 interval 1h-->
<!--<TimeBasedTriggeringPolicy modulate="true" interval="1" />-->
<!--按照文件大小切割文件,切割跨度为3KB ,单位还可以为MB,GB,TB-->
<!--<SizeBasedTriggeringPolicy size="3KB" />-->
<!--同一时刻只允许存在10个文件-->
<!--<DefaultRolloverStrategy max="10"/>-->
</RollingFile>
<RollingFile name="RollingFileError" fileName="${LOG_HOME}/${projectName}/${projectName}.log"
filePattern="${LOG_HOME}/${projectName}/${projectName}.log.%d{yyyy-MM-dd hh-mm}">
<ThresholdFilter level="ERROR" />
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss sss} %-5p %t %l(%r) %m%n" />
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!--过滤spring跟mmybatis的繁多的DEBUG信息-->
<logger name="org.springframework" level="INFO" />
<logger name="org.mybatis" level="INFO" />
<root level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="RollingFileDebug" />
<appender-ref ref="RollingFileError" />
</root>
</Loggers>
</Configuration>
monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.
? ? 对于某些高并发的server,高并发下磁盘io消耗相当严重,我们可以使用log4j2异步Appender,通过在单独的线程中执行I / O操作来提高应用程序的性能.。异步记录器是Log4j 2中的一个新增功能。它们的目标是尽快从调用Logger.log返回到应用程序。您可以选择使所有Loggers异步或使用同步和异步Logger的混合。使所有记录器异步将提供最佳性能,同时混合给你更多的灵活性。
尽管异步日志记录可以带来显着的性能优势,但在某些情况下,您可能需要选择同步日志记录。本节介绍了一些异步日志记录的权衡。
优点
峰值吞吐量更高。使用异步记录器,您的应用程序可以以6至68倍的同步记录器速率记录消息。
这对偶尔需要记录消息突发的应用程序尤其有意义。异步日志记录可以通过缩短等待时间来防止或抑制延迟峰值,直到可记录下一条消息。如果队列大小配置得足够大以处理突发事件,异步日志记录将有助于防止应用程序在突然增加活动期间落后(尽可能多)。
Log4j-2.9及更高版本需要classpath上的disruptor-3.3.4.jar或更高版本。在Log4j-2.9之前,需要disruptor-3.0.0.jar或更高版本。
这是最简单的配置,并提供最佳性能。要使所有记录器异步,请将disruptor?jar添加到类路径中,并将系统属性log4j2.contextSelector设置?为org.apache.logging.log4j.core.async.AsyncLoggerContextSelector。
默认情况下,位置不会通过异步记录器传递到I / O线程。如果您的某个布局或自定义过滤器需要位置信息,则需要在所有相关记录器(包括根记录器)的配置中设置“includeLocation = true”。
? 贴码??????? ??<?xml version="1.0" encoding="UTF-8"?>
<!-- Don't forget to set system property
-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
to make all loggers asynchronous. -->
<Configuration status="WARN">
<Appenders>
<!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
<RandomAccessFile name="RandomAccessFile" fileName="async.log" immediateFlush="false" append="false">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern>
</PatternLayout>
</RandomAccessFile>
</Appenders>
<Loggers>
<Root level="info" includeLocation="false">
<AppenderRef ref="RandomAccessFile"/>
</Root>
</Loggers>
</Configuration>
? ??
当使用AsyncLoggerContextSelector使所有记录器异步时,请确保在配置中使用普通的?<root>和<logger>元素。AsyncLoggerContextSelector将确保所有记录器都是异步的,使用的机制与配置<asyncRoot>?或<asyncLogger>时发生的机制不同。。后面的元素用于与同步记录器混合异步。如果同时使用这两种机制,则最终会有两个后台线程,您的应用程序将日志消息传递给线程A,线程A将消息传递给线程B,线程B最终将消息记录到磁盘。这有效,但中间会有不必要的步骤。
您可以使用几个系统属性来控制异步日志记录子系统的各个方面。其中一些可用于调整日志记录性能。
还可以通过创建名为log4j2.component.properties的文件并在应用程序的类路径中包含此文件来指定以下属性?。
请注意,系统属性在Log4j 2.10.0中被重新命名为更一致的样式。所有旧的属性名称仍支持这里记录。
系统属性 | 默认值 | 描述 |
---|---|---|
log4j2.asyncLoggerExceptionHandler | 默认处理器 | 实现com.lmax.disruptor.ExceptionHandler?接口的类的完全限定名称。该类需要有一个公共的零参数构造函数。如果指定,则在记录消息时发生异常时将通知此类。 如果未指定,则默认异常处理程序将向标准错误输出流打印消息和堆栈跟踪。 |
log4j2.asyncLoggerRingBufferSize | 256 * 1024 | 异步日志子系统使用的RingBuffer中的大小(槽数)。使此值足够大以处理活动爆发。最小尺寸为128. RingBuffer将在首次使用时预先分配,并且在系统的使用期限内不会增长或缩小。 当应用程序的记录速度快于底层appender可以跟上足够长的时间以填满队列时,行为将由AsyncQueueFullPolicy确定?。 |
log4j2.asyncLoggerWaitStrategy | 时间到 | 有效值:阻止,超时,睡眠,收益。? Block是一种为I / O线程等待日志事件使用锁定和条件变量的策略。当吞吐量和低延迟不如CPU资源重要时,可以使用Block。建议用于资源受限/虚拟化环境。? Timeout是Block策略的一种变体,它会周期性地从锁定状态await()调用中唤醒。这确保了如果通知被遗漏,消费者线程不会被卡住,但会以小的延迟时间(默认10ms)恢复。? 睡觉是一种最初旋转的策略,然后使用Thread.yield(),并最终停放OS和JVM在I / O线程等待日志事件时允许的最小纳米数量。睡眠是性能和CPU资源之间的良好折中。此策略对应用程序线程的影响非常低,以换取实际获取消息记录的一些额外延迟。? Yield是一种使用Thread.yield()等待初始旋转后的日志事件的策略。良率是性能和CPU资源之间的良好折衷,但可能会使用比休眠更多的CPU,以便更快地将消息记录到磁盘。 |
log4j2.asyncLoggerThreadNameStrategy | CACHED | 有效值:CACHED,UNCACHED。? 默认情况下,AsyncLogger将线程名称缓存在ThreadLocal变量中以提高性能。如果您的应用程序在运行时修改了线程名称(使用Thread.currentThread()。setName())并且您想要查看日志中反映的新线程名称,请指定UNCACHED选项?。? |
log4j2.clock | SystemClock | 实现org.apache.logging.log4j.core.util.Clock?接口,用于在所有记录器异步时对日志事件进行时间戳记。? CachedClock是一种针对低延迟应用程序的优化,其中时间戳由每毫秒或每1024个日志事件(以先到者为准)在后台线程中更新其内部时间的时钟生成。这会稍微减少日志记录延迟,但会以记录的时间戳中的某些精度为代价。除非记录许多事件,否则在日志时间戳之间可能会看到10-16毫秒的“跳跃”。WEB应用程序警告:使用后台线程可能会导致Web应用程序和OSGi应用程序出现问题,因此不推荐将CachedClock用于此类应用程序。 您还可以指定实现Clock接口的自定义类的完全限定类名称?。 |
即使在底层appender无法跟上日志记录速率和队列填满的情况下,也可以使用少数系统属性来维护应用程序吞吐量。查看系统属性log4j2.asyncQueueFullPolicy和?log4j2.discardThreshold的详细信息?。
Log4j-2.9及更高版本需要classpath上的disruptor-3.3.4.jar或更高版本。在Log4j-2.9之前,需要disruptor-3.0.0.jar或更高版本。没有必要将系统属性“Log4jContextSelector”设置为任何值。
同步和异步记录器可以配置组合。这样可以在性能略有下降的情况下提供更大的灵活性(与使所有记录器异步)相比。使用<asyncRoot>或<asyncLogger>?配置元素来指定需要异步的记录器。配置只能包含一个根记录器(<root>?或<asyncRoot>元素),但可以组合异步和非异步记录器。例如,包含<asyncLogger>元素的配置文件也可以包含<root>和?<?同步记录器的元素。
默认情况下,位置不会通过异步记录器传递到I / O线程。如果您的某个布局或自定义过滤器需要位置信息,则需要在所有相关记录器(包括根记录器)的配置中设置“includeLocation = true”。
混合异步记录器的配置可能如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!-- No need to set system property "log4j2.contextSelector" to any value
when 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>
您可以使用几个系统属性来控制异步日志记录子系统的各个方面。其中一些可用于调整日志记录性能。
还可以通过创建名为log4j2.component.properties的文件并在应用程序的类路径中包含此文件来指定以下属性?。
请注意,系统属性在Log4j 2.10中被重新命名为更一致的样式。所有旧的属性名称仍支持这里记录。
系统属性 | 默认值 | 描述 |
---|---|---|
log4j2.asyncLoggerConfigExceptionHandler | 默认处理器 | 实现com.lmax.disruptor.ExceptionHandler?接口的类的完全限定名称。该类需要有一个公共的零参数构造函数。如果指定,则在记录消息时发生异常时将通知此类。 如果未指定,则默认异常处理程序将向标准错误输出流打印消息和堆栈跟踪。 |
log4j2.asyncLoggerConfigRingBufferSize | 256 * 1024 | 异步日志子系统使用的RingBuffer中的大小(槽数)。使此值足够大以处理活动爆发。最小尺寸为128. RingBuffer将在首次使用时预先分配,并且在系统的使用期限内不会增长或缩小。 当应用程序的记录速度快于底层appender可以跟上足够长的时间以填满队列时,行为将由AsyncQueueFullPolicy确定?。 |
log4j2.asyncLoggerConfigWaitStrategy | 时间到 | 有效值:阻止,超时,睡眠,收益。? Block是一种为I / O线程等待日志事件使用锁定和条件变量的策略。当吞吐量和低延迟不如CPU资源重要时,可以使用Block。建议用于资源受限/虚拟化环境。? Timeout是Block策略的一种变体,它会周期性地从锁定状态await()调用中唤醒。这确保了如果通知被遗漏,消费者线程不会被卡住,但会以小的延迟时间(默认10ms)恢复。? 睡觉是一种最初旋转的策略,然后使用Thread.yield(),并最终停放OS和JVM在I / O线程等待日志事件时允许的最小纳米数量。睡眠是性能和CPU资源之间的良好折中。此策略对应用程序线程的影响非常低,以换取实际获取消息记录的一些额外延迟。? Yield是一种使用Thread.yield()等待初始旋转后的日志事件的策略。良率是性能和CPU资源之间的良好折衷,但可能会使用比休眠更多的CPU,以便更快地将消息记录到磁盘。 |
即使在底层appender无法跟上日志记录速率和队列填满的情况下,也可以使用少数系统属性来维护应用程序吞吐量。查看系统属性log4j2.asyncQueueFullPolicy和?log4j2.discardThreshold的详细信息?。
如果其中一个布局配置了与位置相关的属性(如HTML?locationInfo)或其中一个模式%C或$ class,?%F或%file,?%l或%location,?%L或%line,?%M或%方法,Log4j将拍摄堆栈的快照,然后走栈跟踪以查找位置信息。
这是一项昂贵的操作:对同步记录仪来说,速度要慢1.3至5倍。同步记录器在进行堆栈快照前尽可能长时间地等待。如果不需要位置,则不会拍摄快照。
但是,异步记录器需要在将日志消息传递给另一个线程之前作出此决定;?该点后的位置信息将会丢失。采用堆栈跟踪快照对异步记录器的性能影响甚至更高:使用位置记录比没有位置记录慢30-100倍。出于这个原因,异步记录器和异步appender默认不包含位置信息。
您可以通过指定includeLocation =“true”来覆盖记录器或异步appender配置中的缺省行为。
下图比较了 同步记录器,异步appender(应该是混合异步)和异步记录器(全异步)的吞吐量。这是所有线程的总吞吐量。在使用64个线程的测试中,异步记录器比异步记录器快12倍,比同步记录器快68倍。
异步记录器的吞吐量随着线程数量的增加而增加,而无论执行日志记录的线程数量如何,同步记录器和异步appender都具有或多或少的恒定吞吐量。
我们还将异步记录器的峰值吞吐量与其他日志记录软件包中提供的同步记录器和异步appender进行了比较,具体来说,log4j-1.2.17和logback-1.0.10具有类似的结果。对于异步appender,当添加更多线程时,所有线程的总日志吞吐量一起保持大致不变。异步记录器可以更有效地利用多线程场景中机器上可用的多个内核。
????????
? ? ? ??
????????