likes
comments
collection
share

深入理解 Logback 日志框架:从 logger.info()到日志输出的完整生命周期Logback 是 Java

作者站长头像
站长
· 阅读数 30

Logback 是 Java 中最常用的日志框架之一,广泛应用于各种项目中。了解 Logback 的内部机制,尤其是在调用 logger.info("message") 之后,日志是如何被处理、过滤,并最终输出的,对于优化和扩展日志系统至关重要。本文将结合各个细节,从日志调用开始,一直到日志输出,详细剖析 Logback 的整个生命周期,包括涉及的类、方法及其处理过程。

一、Logback 日志处理的整体流程

当在代码中调用 logger.info("message") 时,Logback 背后执行了一系列复杂的操作,这些操作大致可以分为以下几个步骤:

  1. 调用日志方法:通过 SLF4J 提供的 Logger 接口调用日志方法(如 logger.info("message"))。
  2. 日志事件封装:创建一个 LoggingEvent 对象,封装日志信息,如消息内容、日志级别、时间戳、线程信息等。
  3. 日志过滤:日志事件在传递给附加器(Appender)之前,经过配置的 TurboFilterFilter 进行过滤。
  4. 日志输出:经过过滤的日志事件被 Appender 输出到相应的目标(如控制台、文件等)。

深入理解 Logback 日志框架:从 logger.info()到日志输出的完整生命周期Logback 是 Java

二、LoggingEvent 的创建与属性封装

LoggingEvent 是 Logback 用于封装日志事件的核心类。通过 logger.info("message") 方法调用后,会创建一个 LoggingEvent 对象,并在构造函数中初始化所有相关属性,包括:

  • 时间戳(timeStamp):通过 System.currentTimeMillis() 方法获取当前时间戳,并赋值给 timeStamp 属性。
  • 线程名称(threadName):通过 Thread.currentThread().getName() 方法获取当前线程的名称,并赋值给 threadName 属性。
  • 日志级别(level):日志级别(如 INFO、DEBUG)由调用方法时传递的参数决定,并封装到 LoggingEvent 中。
  • 日志消息(message):日志内容作为参数传递,并封装在 LoggingEvent 对象中。
  • 可变参数(params):如果日志记录方法包含可变参数,这些参数也会被封装进 LoggingEvent 中。

这些属性的封装发生在 LoggingEvent 构造函数中,确保了日志事件包含了所有必要的上下文信息。

三、日志过滤的执行:FilterTurboFilter

在 Logback 中,FilterTurboFilter 都用于日志事件的过滤,但它们的执行顺序和作用范围不同:

  • TurboFilterTurboFilter 是在日志系统的更高层次进行全局过滤。它的设计初衷是为了提供更高性能的过滤机制,适用于全局性或性能敏感的过滤逻辑。TurboFilter 在日志事件被传递到具体的 Appender 之前就会被执行,因此它的决策能够避免不必要的日志处理,提升性能。

    可以通过继承 TurboFilter 类来创建自定义的过滤器,例如:

    public class CustomTurboFilter extends TurboFilter {
        @Override
        public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
            if (marker != null && marker.contains("SECURITY")) {
                return FilterReply.ACCEPT;
            }
            return FilterReply.NEUTRAL;
        }
    }
    

    该过滤器基于 Marker 值进行过滤,只有当 Marker 包含 “SECURITY” 时才接受日志。

  • FilterFilter 是在具体的 Appender 上进行配置的。每个 Appender 可以拥有多个 Filter,这些过滤器会在日志事件被真正输出前对其进行判断。如果需要在日志事件到达 Appender 之前对其进行修改或阻止输出,可以通过自定义 Filter 实现。

    举例来说,如果希望在日志事件传递给 Appender 之前修改其日志级别,可以实现一个自定义 Filter 并在其中进行修改:

    public class LevelChangingFilter extends Filter<ILoggingEvent> {
        @Override
        public FilterReply decide(ILoggingEvent event) {
            if (event.getLevel().equals(Level.INFO)) {
                event.setLevel(Level.DEBUG); // 修改日志级别
            }
            return FilterReply.NEUTRAL;
        }
    }
    

    但是,如果通过 Filter 修改了 LoggingEvent 中的 level 属性,且希望在某些情况下跳过执行 Appender,可以返回 FilterReply.DENY,这样就不会继续调用后续的 Appender

四、自定义 TurboFilter 的加载与应用

TurboFilter 的加载发生在 Logback 的初始化阶段。通常,TurboFilter 可以通过 logback.xml 配置文件进行配置,但如果希望自动为所有应用配置自定义的 TurboFilter,可以通过 Spring Boot 自动配置机制实现。

自动配置 TurboFilter

当希望在一个 JAR 包中定义自定义的 TurboFilter,并希望其他项目在加载该 JAR 包后自动应用这个 TurboFilter 时,可以通过以下步骤实现:

  1. 创建自定义 TurboFilter:如前文所述,实现自定义的 TurboFilter

  2. 通过 Spring Boot 自动配置:在 JAR 包中提供自动配置类,并确保在 Spring Boot 项目中加载该类时自动注册 TurboFilter

    @Configuration
    public class LogbackAutoConfiguration {
        @PostConstruct
        public void addCustomTurboFilter() {
            LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
            TurboFilter customTurboFilter = new CustomTurboFilter();
            loggerContext.addTurboFilter(customTurboFilter);
        }
    }
    

    通过此配置,当其他项目加载该 JAR 包时,自定义的 TurboFilter 会被自动注册到日志系统中。

  3. 声明自动配置类:在 spring.factories 文件中声明自动配置类:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.example.logging.LogbackAutoConfiguration
    

五、LoggerContext 的角色与日志系统初始化

LoggerContext 是 Logback 中管理日志相关配置的核心类。在一个 Spring Boot 项目中,LoggerContext 实例是全局唯一的,这意味着整个应用的日志配置都在同一个上下文中进行管理。

LoggerContext 在日志系统初始化时加载配置文件(如 logback.xml),并初始化所有 LoggerAppenderFilterTurboFilter。自定义的日志过滤逻辑(如 TurboFilter)通常需要在日志系统初始化之后,应用其他部分开始记录日志之前执行,以确保这些逻辑生效。

在 Spring Boot 项目中,通过 Spring Boot 自动配置机制,可以在日志系统初始化时将自定义 TurboFilter 添加到 LoggerContext 中,确保日志记录时自定义过滤逻辑得到执行。

六、深入理解日志记录的细节:MarkerLoggingEvent

在日志记录过程中,Marker 是一个重要的概念,用于标记日志事件的特定类别或上下文。虽然 logger.info("message") 方法不直接使用 Marker 参数,但 Logback 提供了支持 Marker 的日志记录方法,例如:

logger.info(marker, "This is a marker log message");

当使用带 Marker 的日志方法时,Marker 会被传递给 LoggingEvent 对象,并在后续的过滤和处理过程中进行处理。

LoggingEvent 对象不仅包含 Marker,还封装了其他关键信息,如时间戳、线程名称、日志级别、日志消息等。这些信息在 LoggingEvent 的构造函数中被初始化,并通过一系列方法进行处理和传递。

七、总结

本文全面探讨了 Logback 日志框架的内部工作原理,涵盖了从 logger.info("message") 到日志输出的全过程。我们深入剖析了 LoggingEvent 的创建与属性封装、FilterTurboFilter 的执行顺序,以及如何通过 Spring Boot 自动配置机制实现自定义日志过滤逻辑。还详细讨论了 LoggerContext 的角色与日志系统初始化、Marker 的处理过程。

通过理解这些底层机制,可以更高效地使用 Logback,尤其是在处理复杂的日志记录需求时。合理的日志系统配置和扩展能够显著提升应用的可维护性、可调试性,并确保日志记录系统的性能与稳定性。

转载自:https://juejin.cn/post/7403627317332869146
评论
请登录