前文已经讲了log4j2的AsyncAppender的实现【log4j2异步日志解读(一)AsyncAppender】,今天我们看看AsyncLogger的实现。

看了这个图,应该很清楚AsyncLogger调用Disruptor,然后直接返回。至于高性能队列 这里已经展开讲了是如何实现的。

AsyncLogger的调用流程

我们来看看AsyncLogger的调用流程,log.info()首先会调用抽象类AbstractLogger,然后调用了Logger的logMessage。

//Logger.java
@Override
public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message,
final Throwable t) {
final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
strategy.log(this, getName(), fqcn, marker, level, msg, t);
}

strategy.log是调用了ReliabilityStrategy接口,日志事件传递到适当的appender的对象的接口,然后调用了LoggerConfig.log()方法,来创建有关记录消息的上下文信息。

//LoggerConfig.java
@PerformanceSensitive("allocation")
public void log(final String loggerName, final String fqcn, final Marker marker, final Level level,
final Message data, final Throwable t) {
List<Property> props = null;
if (!propertiesRequireLookup) {
props = properties;
} else {
if (properties != null) {
props = new ArrayList<>(properties.size());
final LogEvent event = Log4jLogEvent.newBuilder()
.setMessage(data)
.setMarker(marker)
.setLevel(level)
.setLoggerName(loggerName)
.setLoggerFqcn(fqcn)
.setThrown(t)
.build();
for (int i = 0; i < properties.size(); i++) {
final Property prop = properties.get(i);
final String value = prop.isValueNeedsLookup() // since LOG4J2-1575
? config.getStrSubstitutor().replace(event, prop.getValue()) //
: prop.getValue();
props.add(Property.createProperty(prop.getName(), value));
}
}
}
final LogEvent logEvent = logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t);
try {
log(logEvent, LoggerConfigPredicate.ALL);
} finally {
// LOG4J2-1583 prevent scrambled logs when logging calls are nested (logging in toString())
ReusableLogEventFactory.release(logEvent);
}
}

接着我们来看AsyncLoggerConfig.logToAsyncDelegate()方法,首先会调用Disruptor,放入环形队列。如果环形队列阻塞,则执行等待策略。

//AsyncLoggerConfig.java
private void logToAsyncDelegate(LogEvent event) {
if (!isFiltered(event)) {
// Passes on the event to a separate thread that will call
// asyncCallAppenders(LogEvent).
populateLazilyInitializedFields(event);
if (!delegate.tryEnqueue(event, this)) {
//如果获取Disruptor队列需要等待则执行等待策略,这里类似AsyncAppender等待策略
handleQueueFull(event);
}
}
} private void handleQueueFull(final LogEvent event) {
if (AbstractLogger.getRecursionDepth() > 1) { // LOG4J2-1518, LOG4J2-2031
// If queue is full AND we are in a recursive call, call appender directly to prevent deadlock
AsyncQueueFullMessageUtil.logWarningToStatusLogger();
logToAsyncLoggerConfigsOnCurrentThread(event);
} else {
// otherwise, we leave it to the user preference
final EventRoute eventRoute = delegate.getEventRoute(event.getLevel());
// 1、DefaultAsyncQueueFullPolicy---等待队列,转为同步操作策略
// 2、DiscardingAsyncQueueFullPolicy---按照日志等级抛弃日志策略
eventRoute.logMessage(this, event);
}
}

然后再来看看Disruptor写入 的过程。LogEvent是记录消息的上下文信息的接口,然后调用tryPublishEvent去获取环形队列的位置,然后发布数据到环形队列上。这一块具体可以看笔者前文Disruptor源码分析,这里就不展开讨论。

//AsyncLoggerConfigDisruptor.java
@Override
public boolean tryEnqueue(final LogEvent event, final AsyncLoggerConfig asyncLoggerConfig) {
final LogEvent logEvent = prepareEvent(event);
return disruptor.getRingBuffer().tryPublishEvent(translator, logEvent, asyncLoggerConfig);
}

日志的消费过程,定义RingBufferLogEventHandler类实现Disruptor的SequenceReportingEventHandler的onEvent方法,从ringbuffer读取事件进行处理。最后会调用该logger绑定的默认appender输出。

最后提供下笔者测试demo

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Appenders>
<RollingRandomAccessFile name="applicationAppender" fileName="./log/application.log"
filePattern="./log/$${date:yyyy-MM}/common-%d{yyyy-MM-dd}.log.gz"
append="false">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingRandomAccessFile> <Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%p] %t - %l - %m%n"/>
</Console> <!-- AsyncAppender配置 -->
<!--<Async name="asyncTest" blocking="true">-->
<!--<AppenderRef ref="applicationAppender"/>-->
<!--</Async>--> </Appenders> <Loggers>
<!-- AsyncLogger配置 -->
<AsyncLogger name="log4j2" >
<AppenderRef ref="applicationAppender"/>
</AsyncLogger> <Root level="info">
<!--<AppenderRef ref="CONSOLE"/>-->
<AppenderRef ref="applicationAppender"/>
</Root> <!--<Logger name="log4j2" level="debug" additivity="false" >-->
<!--<AppenderRef ref="CONSOLE"/>-->
<!--<AppenderRef ref="applicationAppender"/>-->
<!--</Logger>--> </Loggers>
</Configuration>

总结

1、Log4j 2的异步记录日志在一定程度上提供更好的吞吐量,但是一旦队列已满,appender线程需要等待,这个时候就需要设置等待策略,AsyncAppender是依赖于消费者最序列最后的消费者,会持续等待。至于异步性能图可以看下官方提供的吞吐量比较图,差异很明显。

2、因为AsyncAppender是采用Disruptor,通过环形队列无阻塞队列作为缓冲,多生产者多线程的竞争是通过CAS实现,无锁化实现,可以降低极端大的日志量时候的延迟尖峰,Disruptor 可是号称一个线程里每秒处理600万订单的高性能队列。

log4j2异步日志解读(二)AsyncLogger的更多相关文章

  1. log4j2异步日志解读(一)AsyncAppender

    log4j.logback.log4j2 历史和关系,我们就在这里不展开讲了.直接上干货,log4j2突出于其他日志的优势,异步日志实现. 看一个东西,首先看官网文档 ,因为前面文章已经讲解了disr ...

  2. 如何监控 Log4j2 异步日志遇到写入瓶颈

    如何监控 Log4j2 异步日志遇到写入瓶颈 在之前的一篇文章中(一次鞭辟入里的 Log4j2 异步日志输出阻塞问题的定位),我们详细分析了一个经典的 Log4j2 异步日志阻塞问题的定位,主要原因还 ...

  3. 一次鞭辟入里的 Log4j2 异步日志输出阻塞问题的定位

    一次鞭辟入里的 Log4j2 日志输出阻塞问题的定位 问题现象 线上某个应用的某个实例突然出现某些次请求服务响应极慢的情况,有几次请求超过 60s 才返回,并且通过日志发现,服务线程并没有做什么很重的 ...

  4. log4j2异步日志配置及官方文档的问题澄清

    配置及demo 方法一全部打开 加启动参数 -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextS ...

  5. Log4j2中的同步日志与异步日志

    1.背景 Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger两种方式. 2.Log4j2中的同步日志 所谓同步日 ...

  6. log4j 异步日志问题分析

    1. 常用的DailyRollingFileAppender与RollingFileAppender是否同步? 1.1 代码分析 2. log4j 1.2.x提供了异步appender是什么?Asyn ...

  7. 近期业务大量突增微服务性能优化总结-3.针对 x86 云环境改进异步日志等待策略

    最近,业务增长的很迅猛,对于我们后台这块也是一个不小的挑战,这次遇到的核心业务接口的性能瓶颈,并不是单独的一个问题导致的,而是几个问题揉在一起:我们解决一个之后,发上线,之后发现还有另一个的性能瓶颈问 ...

  8. spring boot:配置druid数据库连接池(开启sql防火墙/使用log4j2做异步日志/spring boot 2.3.2)

    一,druid数据库连接池的功能? 1,Druid是阿里巴巴开发的号称为监控而生的数据库连接池 它的优点包括: 可以监控数据库访问性能 SQL执行日志 SQL防火墙 2,druid的官方站: http ...

  9. spring boot:使用log4j2做异步日志打印(spring boot 2.3.1)

    一,为什么要使用log4j2?     log4j2是log4j的升级版,     升级后更有优势:     性能更强/吞吐量大/支持异步     功能扩展/支持插件/支持自定义级别等     这些优 ...

随机推荐

  1. java文件工具类

    import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.Fi ...

  2. webpack 输出多个文件

    http://react-china.org/t/webpack/1870/2 webpack 文章 entry = { "button": "demo/button/i ...

  3. POJ 3370 Halloween treats(抽屉原理)

    Halloween treats Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6631   Accepted: 2448 ...

  4. 解决Vue打包后背景图片路径错误问题

    1.原因解析 当用vue-cli自动构建项目后,有两种运行方法,分别是: npm run dev : 提供一个开发的环境,自动热更新,资源使用绝对路径,所以可以正常看到背景图片. npm run bu ...

  5. Node.js创建自签名的HTTPS服务器

    https://cnodejs.org/topic/54745ac22804a0997d38b32d  用Node.js创建自签名的HTTPS服务器  发布于 4 年前  作者 eeandrew  6 ...

  6. 我是怎样自学 Android 的?

    1. Java知识储备 本知识点不做重点解说: 对于有基础的同学推荐看<Java编程思想>,巩固基础,查漏补全,了解并熟悉很多其它细节知识点. 对于没有基础的同学推荐看一本Java基础的书 ...

  7. java中的ShortBuffer

    一.概述 java.lang.Object java.nio.Buffer java.nio.ShortBuffer public abstract class ShortBuffer extends ...

  8. 另存为Alt-F-A ©┗ 最近使用的文档/选项

    另存为Alt-F-A © ┗ 最近使用的文档/选项

  9. nohup 程序在后台运营 避免 xshell 卡死 通过 nohup.out分析调取系统命令时的异常分析

    nohup  程序在后台运营 避免 xshell  卡死 [root@admin1 after_fc_distributed]# nohup /root/anaconda3/bin/python da ...

  10. beego4---web项目结构

    app.conf appname = blog1 httpport = runmode = dev controllersmy package controllersmy //跟外面的包名一致 imp ...