前文已经讲了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. [bzoj1717][Usaco2006 Dec]Milk Patterns 产奶的模式_后缀数组_二分答案

    Milk Patterns 产奶的模式 bzoj-1717 Usaco-2006 Dec 题目大意:给定一个字符串,求最长的至少出现了$k$次的子串长度. 注释:$1\le n\le 2\cdot 1 ...

  2. org.springframework.amqp.AmqpIOException: java.net.UnknownHostException: guest解决

    org.springframework.amqp.AmqpIOException: java.net.UnknownHostException: guest 由于在yml文件中配置的时候误将passw ...

  3. 输入一个URL之后。。。

    1.输入URL2.浏览器去浏览器缓存.系统缓存.路由器缓存查找缓存记录,有则直接访问URL对应的IP,无则下一步3.DNS解析URL,获得对应的IP4.浏览器通过TCP/IP三次握手连接服务器5.客户 ...

  4. 洛谷 P1122 最大子树和

    P1122 最大子树和 题目描述 小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题.一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的 ...

  5. Orcle定时生成表数据作业

    --建表 create table table41( id ) not null, --主键 col1 ), col2 ), col3 ), col4 int, col5 timestamp, col ...

  6. ubuntu12.04+cuda6.0+opencv2.4.9

    更新了cuda之后,opencv的gpu模块又要重新编译了,这个地方有一个疑问,我对cuda6.0装了两次,第一次装好之后,没有配一个bumblebee,重装了cuda6.0之后,发现原来编译的ope ...

  7. iOS_开发中遇到的那些问题_1

    [自编号:60][AutoLayout中,怎样让ImageView保持固定的宽高比?比如1:1] 先将imageViewframe手动写成:宽20,高20,再勾选Aspect Ratio加入宽高比约束 ...

  8. nyist oj 19 擅长排列的小明(dfs搜索+STL)

    擅长排列的小明 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描写叙述 小明十分聪明.并且十分擅长排列计算.比方给小明一个数字5,他能立马给出1-5按字典序的全排列,假设你想 ...

  9. Linux学习日志--文件搜索命令

    开头总结: 学习了Linux中的文件搜索命令find和locate,系统搜索命令whereis 和which ,字符串搜索命令grep,find和locate的差别和使用方法格式,什么是path环境变 ...

  10. Java VS .NET:Java与.NET的特点对比 单点登录(SSO)的设计

    一.前言 为什么要写Java跟.NET对比? .NET出生之后就带着Java的影子.从模仿到创新,.NET平台也越来越成熟.他们不同的支持者也经常因为孰弱孰强的问题争论不休.但是本文并不是为了一分高下 ...