一、常见日志框架
日志框架简单比较(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,而且配置更加简单灵活。
二、log4j/logback/log4j2四大件:Logger、Appender、Layout、Filter
2.1 简介
-
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、异步AsyncLogger
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<Serializable> 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 |
性能最好;占用宿主机资源多一些,可能存在消息丢失 |
⭐️ ⭐️ ⭐️ ⭐️ |