日志框架简单比较(slf4j、j.u.l、log4j、logback、log4j2 )
slf4j:slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback、log4j2)。
j.u.l:j.u.l是java.util.logging包的简称,是JDK在1.4版本中引入的Java原生日志框架。
log4j:log4j是apache实现的一个开源日志组件。
logback:logback也是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现。
log4j2:log4j2是log4j和logback的改进版,采用了一些新技术(无锁异步、Disruptor等),使得日志的吞吐量、性能比log4j提高了10倍,并解决了一些死锁的bug,而且配置更加简单灵活。
Logger:日志记录器,负责收集处理日志记录 (谁+如何处理日志)
Appender:日志输出目的地,负责日志的输出 (输出到什么地方)
Layout:日志格式化,负责对输出的日志格式化(以什么格式输出)
Filter:日志过滤,负责过滤掉不需要输出的日志(满足条件才输出)
即logger负责收集日志,交给对应的appender,然后appender利用filter对日志进行过滤,并利用layout将日志按照一定的格式进行输出。

log4j2的logger可以分为两类:一类是所有logger的父类,叫root;另外一类就是自定义logger。后者的appender根据logger的参数additivity(默认为true)决定是否要叠加root的appender,即默认情况下,一条日志既会输出到logger对应的所有appender中,也会输出到root对应的所有appender中。logger的级别是其自身定义的级别,和root的级别没关系。
logger收到业务层打印的日志后,会将日志封装成一个LogEvent对象(增加时间戳等信息),最终交给logger对应的所有Appender来进行输出。这里的同步异步是指logger将LogEvent交给Appender的方式:同步是指logger自身一个个地遍历appenders,将LogEvent传递给每个appenders(appender可能会有一个BlockingQueue来存储LogEvent,当然也可以像ConsoleAppender那样啥都没有):
- //同步logger遍历将LogEvent传递给appenders:LoggerConfig.java类
- protected void callAppenders(final LogEvent event) {
- final AppenderControl[] controls = appenders.get();
- //noinspection ForLoopReplaceableByForEach
- for (int i = 0; i < controls.length; i++) {
- controls[i].callAppender(event);
- }
- }
异步logger是指logger不需要遍历appenders,而是将LogEvent放到一个统一的“地方”,然后再由一个独立的分发器将LogEvent取出来并分发给对应的appenders。以log4j2为例,这个独立的分发器叫Disruptor,而这个统一的“地方”就是位于Disruptor里的RingBuffer:

- //AsyncLoggerDisruptor将LogEvent放到RingBuffer,无阻塞:AsyncLoggerDisruptor.java
- boolean tryPublish(final RingBufferLogEventTranslator translator) {
- try {
- return disruptor.getRingBuffer().tryPublishEvent(translator);
- } catch (final NullPointerException npe) {
- // LOG4J2-639: catch NPE if disruptor field was set to null in stop()
- logWarningOnNpeFromDisruptorPublish(translator);
- return false;
- }
- }
关于Disruptor的介绍,详见:Disruptor核心原理、源码解析_邋遢的流浪剑客的博客-CSDN博客_disruptor源码。
这里的同步Appender和异步Appender是指Appender在将LogEvent输出的时候,是同步还是异步。比如ConsoleAppender就是同步的,而logback和log4j2的XMDFileAppender就是异步的。
-
- @Plugin(name = "XMDFile", category = "Core", elementType = "appender", printObject = true)
- public final class XMDFileAppender extends AbstractAppender {
- private final BlockingQueue
queue; - //异步线程,从queue中曲LogEvent来消费
- private AsyncThread thread;
- //略
- }
回顾下前面一小节,logger的同步异步是指将logEvent交接给appender的方式,而appender的同步异步是指logEvent输出的方式。由此我们得到以下四种组合:
| Logger类型 |
Appender类型 |
性能 |
log4j2默认选型 |
推荐指数 |
|---|---|---|---|---|
| Logger |
Appender |
性能最差 |
默认使用的是同步Logger; Appender取决于用户在log4j2.xml中的配置 |
⭐️ |
| AsyncLogger |
Appender |
性能一般 |
⭐️ ⭐️ |
|
| Logger |
AsyncAppender |
性能较好;占用宿主机资源少一些 |
⭐️ ⭐️ ⭐️ ⭐️ ⭐️ |
|
| AsyncLogger |
AsyncAppender |
性能最好;占用宿主机资源多一些,可能存在消息丢失 |
⭐️ ⭐️ ⭐️ ⭐️ |