• 妈妈再也不用担心我搞不懂——log4j/logback/log4j2原理了


    一、常见日志框架

    日志框架简单比较(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那样啥都没有):

    1. //同步logger遍历将LogEvent传递给appenders:LoggerConfig.java类
    2. protected void callAppenders(final LogEvent event) {
    3. final AppenderControl[] controls = appenders.get();
    4. //noinspection ForLoopReplaceableByForEach
    5. for (int i = 0; i < controls.length; i++) {
    6. controls[i].callAppender(event);
    7. }
    8. }

    异步logger是指logger不需要遍历appenders,而是将LogEvent放到一个统一的“地方”,然后再由一个独立的分发器将LogEvent取出来并分发给对应的appenders。以log4j2为例,这个独立的分发器叫Disruptor,而这个统一的“地方”就是位于Disruptor里的RingBuffer:

    1. //AsyncLoggerDisruptor将LogEvent放到RingBuffer,无阻塞:AsyncLoggerDisruptor.java
    2. boolean tryPublish(final RingBufferLogEventTranslator translator) {
    3. try {
    4. return disruptor.getRingBuffer().tryPublishEvent(translator);
    5. } catch (final NullPointerException npe) {
    6. // LOG4J2-639: catch NPE if disruptor field was set to null in stop()
    7. logWarningOnNpeFromDisruptorPublish(translator);
    8. return false;
    9. }
    10. }

    关于Disruptor的介绍,详见:Disruptor核心原理、源码解析_邋遢的流浪剑客的博客-CSDN博客_disruptor源码

    这里的同步Appender和异步Appender是指Appender在将LogEvent输出的时候,是同步还是异步。比如ConsoleAppender就是同步的,而logback和log4j2的XMDFileAppender就是异步的。

    1. @Plugin(name = "XMDFile", category = "Core", elementType = "appender", printObject = true)
    2. public final class XMDFileAppender extends AbstractAppender {
    3. private final BlockingQueue queue;
    4. //异步线程,从queue中曲LogEvent来消费
    5. private AsyncThread thread;
    6. //略
    7. }

    回顾下前面一小节,logger的同步异步是指将logEvent交接给appender的方式,而appender的同步异步是指logEvent输出的方式。由此我们得到以下四种组合:

    Logger类型

    Appender类型

    性能

    log4j2默认选型

    推荐指数

    Logger

    Appender

    性能最差

    默认使用的是同步Logger;

    Appender取决于用户在log4j2.xml中的配置

    ⭐️

    AsyncLogger

    Appender

    性能一般

    ⭐️ ⭐️

    Logger

    AsyncAppender

    性能较好;占用宿主机资源少一些

    ⭐️ ⭐️ ⭐️ ⭐️ ⭐️

    AsyncLogger

    AsyncAppender

    性能最好;占用宿主机资源多一些,可能存在消息丢失

    ⭐️ ⭐️ ⭐️ ⭐️

  • 相关阅读:
    Spring:IOC/DI注解开发(7)
    485modbus转profinet网关在混料配料输送系统应用博图配置案例
    解决Redis启动:未找到命令的问题
    量化交易全流程(一)
    【项目源码】一套基于springboot+Uniapp框架开发的智慧医院3D人体导诊系统源码
    LLM - 大模型速递 Baichuan2 快速入门
    汇编层面来理解C语言的值传递和引用传递的区别
    Linux学习之HIS部署(4)
    Yolov5进阶之五GPU环境搭建
    DBA常用命令
  • 原文地址:https://blog.csdn.net/Saintyyu/article/details/128148025