log4j2最佳实践

 最佳实践

使用 Log4j API 时存在一些普遍存在的不良做法。下面我们将介绍最常见的问题并了解如何解决它们。有关完整列表,请参阅Log4j API 最佳实践页面

不要使用toString()

  • 不要在参数中使用Object#toString() ,它是多余的!

    /* BAD! */ LOGGER.info("userId: {}", userId.toString());
  • 底层消息类型和布局将处理参数:

    /* GOOD */ LOGGER.info("userId: {}", userId);

将异常作为最后一个额外参数传递

  • 不要调用Throwable#printStackTrace() !这不仅可以规避日志记录,还可能泄露敏感信息!

    /* BAD! */ exception.printStackTrace();
  • 不要使用Throwable#getMessage() !这可以防止日志事件因异常而变得丰富。

    /* BAD! */ LOGGER.info("failed", exception.getMessage());
    /* BAD! */ LOGGER.info("failed for user ID `{}`: {}", userId, exception.getMessage());
  • 不要同时提供Throwable#getMessage()Throwable本身!这会导致日志消息因重复的异常消息而变得臃肿。

    /* BAD! */ LOGGER.info("failed for user ID `{}`: {}", userId, exception.getMessage(), exception);
  • 将异常作为最后一个额外参数传递:

    /* GOOD */ LOGGER.error("failed", exception);
    /* GOOD */ LOGGER.error("failed for user ID `{}`", userId, exception);

不要使用字符串连接

如果您在记录时使用String连接,那么您正在做一些非常错误和危险的事情!

  • 不要使用String连接来格式化参数!这避免了按消息类型和布局处理参数。更重要的是,这种方式很容易受到攻击! 想象一下用户提供的userId包含以下内容: placeholders for non-existing args to trigger failure: {} {} {dangerousLookup}

    /* BAD! */ LOGGER.info("failed for user ID: " + userId);
  • /* GOOD */ LOGGER.info("failed for user ID `{}`", userId);

    最佳实践

    使用 Log4j API 时存在一些普遍存在的不良做法。让我们尝试了解最常见的问题并看看如何解决它们。

    不要使用toString()

    • 不要在参数中使用Object#toString() ,它是多余的!

      /* BAD! */ LOGGER.info("userId: {}", userId.toString());
    • 底层消息类型和布局将处理参数:

      /* GOOD */ LOGGER.info("userId: {}", userId);

    将异常作为最后一个额外参数传递

    • 不要调用Throwable#printStackTrace() !这不仅可以规避日志记录,还可能泄露敏感信息!

      /* BAD! */ exception.printStackTrace();
    • 不要使用Throwable#getMessage() !这可以防止日志事件因异常而变得丰富。

      /* BAD! */ LOGGER.info("failed", exception.getMessage());
      /* BAD! */ LOGGER.info("failed for user ID `{}`: {}", userId, exception.getMessage());
    • 不要同时提供Throwable#getMessage()Throwable本身!这会导致日志消息因重复的异常消息而变得臃肿。

      /* BAD! */ LOGGER.info("failed for user ID `{}`: {}", userId, exception.getMessage(), exception);
    • 将异常作为最后一个额外参数传递:

      /* GOOD */ LOGGER.error("failed", exception);
      /* GOOD */ LOGGER.error("failed for user ID `{}`", userId, exception);

    不要使用字符串连接

    如果您在记录时使用String连接,那么您正在做一些非常错误和危险的事情!

    • 不要使用String连接来格式化参数!这避免了按消息类型和布局处理参数。更重要的是,这种方式很容易受到攻击! 想象一下用户提供的userId包含以下内容: placeholders for non-existing args to trigger failure: {} {} {dangerousLookup}

      /* BAD! */ LOGGER.info("failed for user ID: " + userId);
    •   使用消息参数

      /* GOOD */ LOGGER.info("failed for user ID `{}`", userId);

    使用Supplier传递计算成本较高的参数

    如果日志语句的一个或多个参数在计算上是昂贵的,那么在知道它们的结果可以被丢弃的情况下评估它们是不明智的。考虑以下示例:

    /* BAD! */ LOGGER.info("failed for user ID `{}` and role `{}`", userId, db.findUserRoleById(userId));

    如果创建的日志事件无论如何都会被丢弃,那么数据库查询(即db.findUserNameById(userId) )可能会成为一个严重的瓶颈 - 也许该记录器不接受INFO级别,或者由于某些其他过滤。

    • 解决这个问题的传统方法是对日志语句进行级别保护:

      /* OKAY */ if (LOGGER.isInfoEnabled()) { LOGGER.info(...); }

      虽然这适用于由于级别不足而可能丢弃消息的情况,但此方法仍然容易出现其他过滤情况;例如,关联的标记可能不被接受。

    • 使用Supplier传递包含计算上昂贵的项目的参数:

      /* GOOD */ LOGGER.info("failed for user ID `{}` and role `{}`", () -> userId, () -> db.findUserRoleById(userId));
    • 使用Supplier传递包含计算上昂贵的项目的消息及其参数:

      /* GOOD */ LOGGER.info(() -> new ParameterizedMessage("failed for user ID `{}` and role `{}`", userId, db.findUserRoleById(userId)));

    线程上下文

    就像Java 的ThreadLocal一样,线程上下文有助于将信息与执行线程关联起来,并使日志系统的其余部分可以访问该信息。线程上下文同时提供了这两种功能

    • 映射结构 – 称为线程上下文映射映射诊断上下文 (MDC)

    • 堆栈结构 - 称为线程上下文堆栈嵌套诊断上下文 (NDC)

      贮存:

    ThreadContext.put("ipAddress", request.getRemoteAddr()); 
    ThreadContext.put("hostName", request.getServerName()); 
    ThreadContext.put("loginId", session.getAttribute("loginId")); 
    
    void performWork() {
        ThreadContext.push("performWork()"); 
    
        LOGGER.debug("Performing work"); 
        // Perform the work
    
        ThreadContext.pop(); 
    }
    
    ThreadContext.clear(); 
      向线程上下文映射添加属性
      将属性推送到线程上下文堆栈
      添加的属性稍后可用于过滤日志事件、在布局中提供额外信息等。
      从线程上下文堆栈中弹出最后推送的属性
      清除线程上下文(对于堆栈和映射!)

    阅读有关线程上下文的更多信息……

      级别

    日志级别用于按严重性对日志事件进行分类并控制日志的详细程度。它们是Log4j API 提供的众多鱼类标记功能之一。使用级别,您可以过滤掉不太重要的日志并专注于最关键的日志。

    Log4j 包含以下预定义级别:

    表 1. 标准日志级别
      姓名   优先事项

    OFF [见注释]

    0

    FATAL

    100

    ERROR

    200

    WARN

    300

    INFO

    400

    DEBUG

    500

    TRACE

    600

    ALL [见注释]

    Integer.MAX_VALUE

     

    OFFALL级别很特殊:它们不应该用于钓鱼标记日志事件。

    Log4j API 实现(例如 Log4j Core)可以在其配置文件中使用OFF来禁用所有日志语句,并使用ALL来启用所有日志语句。

    级别由区分大小写的名称和优先级(类型为int )组成,用于定义比较两者时的顺序。优先级可以在多种情况下使用来表达过滤能力,例如:

    • WARN严重程度低于ERROR

    • WARN不如ERROR具体

    日志级别的入口点是通过Level 。 Log4j API 集成商可以通过StandardLevel获得预定义级别。

    创建标记

      简单的标记

    要创建Marker ,请使用MarkerManager.getMarker()方法在类中创建一个字段:

    private static final Marker SQL_MARKER = MarkerManager.getMarker("SQL");

    由于Marker可以跨多个日志语句重用,因此将其存储在static final字段中使其成为常量。创建后,将其用作日志语句中的第一个参数:

    LOGGER.debug(SQL_MARKER, "SELECT * FROM {}", table);

    如果您使用下面的配置示例,可以在控制台上看到以下日志语句:

    10:42:30.982 (SQL) SELECT * FROM my_table

      家长和孩子标记

    一个标记可以有零个或多个父标记,从而允许标记的层次结构。要创建这样的层次结构,您必须在创建子标记后对Marker对象使用addParents()方法。

    private static final Marker QUERY_MARKER =
            MarkerManager.getMarker("SQL_QUERY").addParents(SQL_MARKER);
    private static final Marker UPDATE_MARKER =
            MarkerManager.getMarker("UPDATE").addParents(SQL_MARKER);

    子标记与简单标记没有区别;必须将它们作为日志记录调用的第一个参数传递。

    LOGGER.debug(QUERY_MARKER, "SELECT * FROM {}", table);
    LOGGER.debug(UPDATE_MARKER, "UPDATE {} SET {} = {}", table, column, value);

    用子标记标记的消息的行为就像同时用子标记及其所有父标记标记一样。如果您使用下面的配置示例,您将在控制台上看到以下日志语句:

    10:42:30.982 (SQL_QUERY[ SQL ]) SELECT * FROM my_table
    10:42:30.982 (SQL_UPDATE[ SQL ]) UPDATE my_table SET column = value

     

    线程上下文

    一样 Java的ThreadLocal , 线程上下文有助于将信息与执行线程关联起来,并使日志系统的其余部分可以访问该信息。线程上下文是Log4j API 提供的众多鱼类标记功能之一。

      用法

    将日志记录相关信息与执行线程关联起来的入口点是 ThreadContext 。它同时提供

    • 映射结构 – 称为线程上下文映射映射诊断上下文 (MDC)  Thread Context Map or Mapped Diagnostic Context  map-structured

    • 堆栈结构 - 称为线程上下文堆栈嵌套诊断上下文 (NDC)  Thread Context Stack or Nested Diagnostic Context  stack-structured 

      贮存:

    ThreadContext.put("ipAddress", request.getRemoteAddr()); 
    ThreadContext.put("hostName", request.getServerName()); 
    ThreadContext.put("loginId", session.getAttribute("loginId")); 
    
    void performWork() {
        ThreadContext.push("performWork()"); 
    
        LOGGER.debug("Performing work"); 
        // Perform the work
    
        ThreadContext.pop(); 
    }
    
    ThreadContext.clear(); 
      向线程上下文映射添加属性
      将属性推送到线程上下文堆栈
      添加的属性稍后可用于过滤日志事件、在布局中提供额外信息等。
      从线程上下文堆栈中弹出最后推送的属性
      清除线程上下文(对于堆栈和映射!)

    自动清除线程上下文

    当将项目放置在线程上下文堆栈或映射上时,有必要在适当的时候再次删除它们。 为了帮助做到这一点,您可以使用 CloseableThreadContext (实施 AutoCloseable )在 try-with-resources 块中:

    try (CloseableThreadContext.Instance ignored = CloseableThreadContext
            .put("ipAddress", request.getRemoteAddr()) 
            .push("performWork()")) { 
    
        LOGGER.debug("Performing work"); 
        // Perform the work
    
    }
    
    // ... 
      进行线程上下文更改,该更改仅在 try-with-resources 块的范围内可见
      添加的属性稍后可用于过滤日志事件、在布局中提供额外信息等。
      在 try-with-resources 块的范围之外,线程上下文的更改将不可见

    初始化线程上下文

    一个常见的用例是通过线程池将单线程执行扇出到多个线程。在这种情况下,您需要将当前线程的上下文克隆到池中的上下文。为此,您可以使用ThreadContextCloseableThreadContext提供的putAll()pushAll()方法:

    LOGGER.debug("Starting background thread for user");
    
    Map<String, String> mdc = ThreadContext.getImmutableContext(); 
    List<String> ndc = ThreadContext.getImmutableStack().asList(); 
    
    executor.submit(() -> {
        try (CloseableThreadContext.Instance ignored = CloseableThreadContext
                .putAll(mdc) 
                .pushAll(ndc)) { 
    
            LOGGER.debug("Processing for user started");
            // ...
    
        }
    });
      获取线程上下文的快照
      初始化后台任务的线程上下文

    自定义线程上下文数据提供者

    ContextDataProvider是一个接口应用程序和库,可用于将其他属性注入到日志事件的上下文数据中。 Log4j 使用java.util.ServiceLoader来定位和加载ContextDataProvider实例。 ThreadContextDataProvider是提供的默认实现。您可以提供自定义ContextDataProvider实现,如下所示:

    1. 在类路径中创建以下文件:

      META-INF/services/org.apache.logging.log4j.core.util.ContextDataProvider
    2. 编写自定义实现的完全限定类名(例如, com.mycompany.CustomContextDataProvider ) 到上一步中创建的文件

    自定义线程上下文映射

    可以通过将log4j2.threadContextMap系统属性设置为扩展自的自定义实现类的完全限定类名来提供自定义线程上下文映射实现 ThreadContextMap 。

    在提供自定义线程上下文映射实现时,建议您也从 ReadOnlyThreadContextMap 也。 通过这种方式,应用程序可以通过以下方式访问您的自定义线程上下文映射实现 ThreadContext.getThreadContextMap() 

    虽然 Log4j 在内部使用Message对象, Logger接口提供了各种快捷方法来创建最常用的消息:

    • 要从String参数创建SimpleMessage ,以下记录器调用是等效的:

      LOGGER.error("Houston, we have a problem.", exception);
      LOGGER.error(new SimpleMessage("Houston, we have a problem."), exception);
    • 要从格式String和对象参数数组创建ParameterizedMessage ,以下记录器调用是等效的:

      LOGGER.error("Unable process user with ID `{}`", userId, exception);
      LOGGER.error(new ParameterizedMessage("Unable process user with ID `{}`", userId), exception);

     

    在大多数情况下,这已经足够了。

    除了满足基于String的消息的用例之外, Message接口抽象还允许用户记录自定义对象。这在某些用例中有效地提供了日志记录的便利。例如,想象一个使用域事件来表示身份验证失败的场景:

    record LoginFailureEvent(String userName, InetSocketAddress remoteAddress) {}

    当开发人员想要记录报告事件的消息时,我们可以看到字符串构造变得更难以阅读:

    LOGGER.info(
            "Connection closed by authenticating user {} {} port {} [preauth]",
            event.userName(),
            event.remoteAddress().getHostName(),
            event.remoteAddress().getPort());

    通过扩展Message接口,开发人员可以简化登录失败的报告:

    record LoginFailureEvent(String userName, InetSocketAddress remoteAddress) implements Message { 
        @Override
        public String getFormattedMessage() { 
            return "Connection closed by authenticating user " + userName() + " "
                    + remoteAddress().getHostName() + " port " + remoteAddress().getPort() + " [preauth]";
        }
        // Other methods
    }
      领域模型需要实现Message接口
      getFormattedMessage()提供要记录的String

    因此, LoginFailureEvent实例的日志记录可以简化如下:

    LOGGER.info(event);

    LocalizedMessage

    LocalizedMessage合并了一个ResourceBundle ,并允许消息模式参数成为包中消息模式的关键。如果未指定捆绑包, LocalizedMessage将尝试查找具有用于记录事件的Logger名称的捆绑包。从包中检索的消息将使用FormattedMessage进行格式化。

     

    LocalizedMessage主要是为了与Log4j 1兼容而提供的。我们建议您在应用程序的表示层(例如客户端 UI)执行日志消息本地化。

    MapMessage

    MapMessage是一个Message实现,它使用String类型的键和值对 Java Map进行建模。 它是传递结构化数据的理想通用消息类型。

    MapMessage实现了MultiformatMessage以便于以多种格式对其内容进行编码。它支持以下格式:

      格式   描述

    XML

      格式为 XML

    JSON

      格式为 JSON

    JAVA

    格式为Map#toString() (默认)

    JAVA_UNQUOTED

    格式为Map#toString() ,但不带引号

    当没有布局时,一些附加程序以不同的方式处理MapMessage :

      JSON 模板布局

    JSON 模板布局MapMessage进行了专门处理,以将它们正确编码为 JSON 对象。

    StructuredDataMessage

      RFC 5424 布局

    StructuredDataMessage主要与RFC 5424 Layout结合使用,后者对StructuredDataMessage进行了专门处理。通过将两者结合起来,用户可以完全控制如何以符合 RFC 5424 的方式对消息进行编码,而 RFC 5424 布局将确保正确注入附加到日志事件的其余信息。

      JSON 模板布局

    由于StructuredDataMessage扩展自MapMessage ,而JSON 模板布局对其进行了专门处理,因此StructuredDataMessage也将由 JSON 模板布局正确编码。

    <turboFilter class="MarkerFilter">
      <Marker>FLOW</Marker>
      <OnMatch>ACCEPT</OnMatch>
    </turboFilter>
    
    <appender name="CONSOLE_DEFAULT" class="ConsoleAppender">
      <filter class="EvaluatorFilter">
        <evaluator class="OnMarkerEvaluator">
          <marker>ENTER</marker>
          <marker>EXIT</marker>
        </evaluator>
        <onMismatch>ACCEPT</onMismatch>
        <onMatch>DENY</onMatch>
      </filter>
      <encoder class="PatternLayoutEncoder">
        <pattern><![CDATA[%d %5p [%t] %c{1} -- %m%n]]></pattern>
      </encoder>
    </appender>
    
    <appender name="CONSOLE_FLOW_ENTER" class="ConsoleAppender">
      <filter class="EvaluatorFilter">
        <evaluator class="OnMarkerEvaluator">
          <marker>ENTER</marker>
        </evaluator>
        <onMismatch>DENY</onMismatch>
        <onMatch>ACCEPT</onMatch>
      </filter>
      <encoder class="PatternLayoutEncoder">
        <pattern><![CDATA[%d %5p [%t] %c{1} => %m%n]]></pattern>
      </encoder>
    </appender>
    
    <appender name="CONSOLE_FLOW_EXIT" class="ConsoleAppender">
      <filter class="EvaluatorFilter">
        <evaluator class="OnMarkerEvaluator">
          <marker>EXIT</marker>
        </evaluator>
        <onMismatch>DENY</onMismatch>
        <onMatch>ACCEPT</onMatch>
      </filter>
      <encoder class="PatternLayoutEncoder">
        <pattern><![CDATA[%d %5p [%t] %c{1} <= %m%n]]></pattern>
      </encoder>
    </appender>
    
    <root level="WARN">
      <appender-ref ref="CONSOLE_DEFAULT"/>
      <appender-ref ref="CONSOLE_FLOW_ENTER"/>
      <appender-ref ref="CONSOLE_FLOW_EXIT"/>
    </root>
      接受标有FLOW日志事件,无论其级别如何
      CONSOLE_DEFAULT附加程序中,排除所有标有ENTEREXIT日志事件
      CONSOLE_DEFAULT附加程序中,日志事件消息将使用-前缀进行格式化
      CONSOLE_FLOW_ENTER附加程序中,仅接受标有ENTER的日志事件
      CONSOLE_FLOW_ENTER附加程序中,日志事件消息将使用前缀进行格式化
      CONSOLE_FLOW_EXIT附加程序中,仅接受标有EXIT的日志事件
      CONSOLE_FLOW_EXIT附加程序中,日志事件消息将使用前缀进行格式化

     

    在一个非常高的水平上,

    • LoggerContext (组合锚点)与Configuration结合创建。两者都可以在第一次与 Log4j 交互时直接(即以编程方式)或间接创建。

    • LoggerContext创建用户出于日志记录目的与之交互的Logger 。

    • AppenderLogEvent传递到目标(文件、套接字、数据库等),通常使用Layout来编码日志事件,并使用AbstractManager来处理目标资源的生命周期。

    • LoggerConfig封装了Logger的配置,就像AppenderAppenderControlAppenderRef一样。

    • Configuration配备有StrSubstitutor等。允许String类型值中的属性替换。

    • 典型的log()调用会按顺序通过类Logger 、 LoggerConfig 、 AppenderControl 、 AppenderAbstractManager触发一系列调用 - 这在图 1中使用绿色箭头进行了描述。

    以下部分详细研究了这种相互作用。

    架构::Apache Log4j --- Architecture :: Apache Log4j

    记录器层次结构

    Log4j Core 具有LoggerConfig分层模型,因此也具有Logger分层模型。如果parent在名称上具有最长的前缀匹配,则称为childLoggerConfig被认为是parent的父级。此匹配区分大小写,并在通过将名称从 中拆分来标记化名称后执行. (点)字符。对于正向名称匹配,标记必须彻底匹配。有关示例,请参见图 7 。

    Example hierarchy of loggers named `X`, `X.Y`, `X.Y.Z`, and `X.YZ`
    图 7. 名为X 、 XY 、 XYZX.YZ的记录器的示例层次结构

    如果LoggerConfig未提供显式级别,它将从其父级继承。同样,如果用户以编程方式请求一个Logger ,其名称没有直接对应的LoggerConfig配置条目及其名称,则将使用父级的LoggerConfig 。

    有关日志级别以及在配置中将其用于过滤目的的更多信息,请参阅级别

     

    下面我们通过级别继承的方式演示LoggerConfig层次结构。也就是说,我们将在各种LoggerConfig设置中检查Logger的有效级别。

    表1. 仅根记录器配置了级别,并且为DEBUG
      记录器名称 分配的LoggerConfig名称   配置级别   有效水平

    root

    root

    DEBUG

    DEBUG

    X

    root

     

    DEBUG

    X.Y

    root

     

    DEBUG

    X.Y.Z

    root

     

    DEBUG

    表 2. 所有记录器都配置有级别
      记录器名称 分配的LoggerConfig   配置级别   有效水平

    root

    root

    DEBUG

    DEBUG

    X

    X

    ERROR

    ERROR

    X.Y

    X.Y

    INFO

    INFO

    X.Y.Z

    X.Y.Z

    WARN

    WARN

    表 3. 除记录器XY外,所有记录器均配置有级别
      记录器名称 分配的LoggerConfig   配置级别   有效水平

    root

    root

    DEBUG

    DEBUG

    X

    X

    ERROR

    ERROR

    X.Y

    X

     

    ERROR

    X.Y.Z

    X.Y.Z

    WARN

    WARN

    表 4. 除记录器XYXYZ外,所有记录器均配置有级别
      记录器名称 分配的LoggerConfig   配置级别   有效水平

    root

    root

    DEBUG

    DEBUG

    X

    X

    ERROR

    ERROR

    X.Y

    X

     

    ERROR

    X.Y.Z

    X

     

    ERROR

    表 5. 除记录器X.YZ外,所有记录器均配置有级别
      记录器名称 分配的LoggerConfig   配置级别   有效水平

    root

    root

    DEBUG

    DEBUG

    X

    X

    ERROR

    ERROR

    X.Y

    X.Y

    INFO

    INFO

    X.YZ

    X

     

    ERROR

    配置文件位置

    在初始化新的记录器上下文(日志记录实现的锚点)时,Log4j Core 会为其分配一个上下文名称,并按以下顺序扫描以下类路径位置以查找配置文件:

    1.   文件名为 log4j2-test<contextName>.<extension>

    2. 名为log4j2-test.<extension>的文件

    3.   文件名为 log4j2<contextName>.<extension>

    4. 名为log4j2.<extension>的文件

    上面的<contextName><extension>占位符具有以下含义

      <上下文名称>

    源自运行时环境的名称:

    • 对于独立的 Java SE 应用程序,它是一个随机标识符。

    • 对于 Web 应用程序,它是从应用程序描述符派生的标识符。有关详细信息,请参阅Log4j Web 应用程序配置

      <扩展>

    ConfigurationFactory支持的文件扩展名。首先搜索扩展的顺序取决于关联的ConfigurationFactory的顺序。有关详细信息,请参阅预定义的ConfigurationFactory插件

    如果未找到配置文件,Log4j Core 将使用DefaultConfiguration ,并且状态记录器会打印警告。默认配置将所有不如log4j2.level严重的消息打印到控制台。

    您可以使用log4j2.configurationFile系统属性覆盖配置文件的位置。在这种情况下,Log4j Core 将从提供的文件名猜测配置文件格式,或者如果扩展名未知,则使用默认配置工厂。

    我们强烈建议您在 Log4j 配置中采用某些最佳实践

    • log4j2-test为前缀的文件只能在测试类路径上使用。

    • 如果您正在开发应用程序,请勿使用多个具有相同名称但扩展名不同的 Log4j 配置文件。也就是说,不要同时提供log4j2.xmllog4j2.json文件。

    • 如果您正在开发库,只需将配置文件添加到测试类路径中。

    预定义的ConfigurationFactory插件

    Log4j Core 使用从ConfigurationFactory扩展的插件来确定支持哪些配置文件扩展、按什么顺序以及如何读取它们。扩展ConfigurationFactory插件中解释了它的内部工作原理以及如何引入自定义实现。

    表 1. 预定义ConfigurationFactory插件支持的配置文件格式
      文件格式   扩大   order

    XML

    xml

    5

    JSON

    json, jsn

    6

    YAML

    yaml, yml

    7

      特性

    properties

    8

    请注意, ConfigurationFactory插件将按降序使用。也就是说,例如,将最后检查 XML 文件格式,作为后备。

     

     

    示例log4j2.yaml的片段
    Configuration:
      Appenders:
        Console: 
          name: "CONSOLE"
          PatternLayout:
            pattern: "%p - %m%n"
        File:
          - name: "MAIN" 
            fileName: "logs/main.log"
            JsonTemplateLayout: {}
          - name: "DEBUG_LOG" 
            fileName: "logs/debug.log"
            PatternLayout:
              pattern: "%d [%t] %p %c - %m%n"
      Loggers:
        Root: 
          level: "INFO"
          AppenderRef:
            - ref: "CONSOLE"
              level: "WARN"
            - ref: "MAIN"
          Logger: 
            name: "org.example"
            level: "DEBUG"
            AppenderRef:
              ref: "DEBUG_LOG"
      使用模式布局配置名为CONSOLE控制台附加程序。
      使用 JSON 模板布局配置名为MAIN的文件附加程序。
      使用模式布局配置名为DEBUG_LOG的文件附加程序。
      INFO级别配置根记录器并将其连接到CONSOLEMAIN附加程序。 CONSOLE附加程序只会记录至少与WARN一样严重的消息。
      DEBUG级别配置名为"org.example"的记录器并将其连接到DEBUG_LOG附加程序。记录器配置为将消息转发到其父级(根附加器)。

    使用上述配置,用于每个日志事件的附加程序列表仅取决于事件的级别和记录器的名称,如下表所示:

      记录器名称   记录事件级别   附加器

    org.example.foo

    WARN

    CONSOLE 、 MAIN 、 DEBUG_LOG

    org.example.foo

    DEBUG

    MAIN , DEBUG_LOG

    org.example.foo

    TRACE

      没有任何

    com.example

    WARN

    CONSOLE MAIN

    com.example

    INFO

    MAIN

    com.example

    DEBUG

      没有任何

    全局配置属性

    主要的Configuration元素有一组属性,可用于调整配置文件的使用方式。下面列出了主要属性。请参阅插件参考以获取完整列表。

    monitorInterval

      类型

    int

      默认值

    0

    确定 Log4j 用于检查配置文件更改的轮询间隔。如果检测到配置文件发生更改,Log4j 会自动重新配置记录器上下文。如果设置为0 ,则禁用轮询。

    Loggers

    Log4j 2 包含多种类型的记录器配置,可以将其添加到配置的Loggers元素中:

    Root

    是接收未定义更具体记录器的所有事件的记录器。

    AsyncRoot

    是在混合同步和异步模式下使用的根记录器的替代实现。

    Logger

    最常见的记录器类型,它从自身和所有子记录器收集日志事件,这些子记录器没有显式配置(请参阅记录器层次结构)。

    AsyncLogger

    相当于Logger ,用于同步异步混合模式

    另请参阅插件参考

    每个配置文件中必须至少有一个RootAsyncRoot元素。其他记录器配置是可选的。

    additivity

      类型

    boolean

      默认值

    true

      适用于

    LoggerAsyncLogger

    如果为true (默认),则此记录器接收到的所有消息也将传输到其 父记录器)。

    level

      类型

    Level

      默认值

    它指定必须记录日志事件的级别阈值。比此设置严重程度低的日志事件将被过滤掉。

    如果您需要额外的过滤,另请参阅过滤器

     

    includeLocation

      类型

    boolean

      默认值

    • false ,如果使用异步ContextSelector 。

    •   否则,

      • 对于RootLogger来说true ,

      • 对于AsyncRootAsyncLoggerfalse 。

    看 log4j2.contextSelector 了解更多详情。

    指定是否允许 Log4j 计算位置信息。如果设置为false ,Log4j 将不会尝试推断日志记录调用的位置,除非使用可用的位置之一显式提供了所述位置 LogBuilder#withLocation() 方法。

     Appender references

    记录器使用附加程序引用来列出用于传递日志事件的附加程序。

     Additional context properties

    记录器可以发出额外的上下文数据,这些数据将与其他上下文数据源(例如ThreadContext )集成。

    个属性的value都会经历两次属性替换

    因此,如果您希望插入随时间变化的值,则必须将$符号加倍,如下例所示。

    Root:
      Property:
        name: "client.address"
        value: "$${web:request.remoteAddress}"
    Logger:
      - name: "org.hibernate"
        Property:
          name: "subsystem"
          value: "Database"
      - name: "io.netty"
        Property:
          name: "subsystem"
          value: "Networking"

    Filters

    Appender references

    许多 Log4j 组件(例如记录器)使用附加程序引用来指定将使用哪些附加程序来传递其事件。

    与 Log4j 1 中的附加程序引用是简单指针不同,在 Log4j 2 中,它们具有额外的过滤功能。

    Appender 引用可以具有以下配置属性和元素:

    ref

      类型

    String

    指定要使用的附加程序的名称。

    level

      类型

    Level

    它指定必须记录日志事件的级别阈值。比此设置严重程度低的日志事件将被过滤掉。

    过滤器

    有关可应用于记录器配置的其他过滤功能,请参阅过滤器

    财产替代Property substitution

    Log4j 提供了一种简单且可扩展的机制,可以使用${name}表达式重用配置文件中的值,例如 Bash、Ant 或 Maven 中使用的表达式。

    可以使用Properties组件将可重用的配置值直接添加到配置文件中。

    Configuration:
      Properties:
        Property:
          - name: "log.dir"
            value: "/var/log"
          - name: "log.file"
            value: "${log.dir}/app.log"

    可扩展的查找机制还可以提供可重用的配置值。有关详细信息,请参阅Lookup 。

    通过使用以下扩展规则,可以在任何配置属性中使用以这种方式定义的配置值:

    ${name}

    如果配置文件的Properties元素具有名为name的属性,则其值将被替换。否则,占位符不会扩展。

     

    如果name包含:字符,则会按以下规则展开。

    ${lookup:name}

    如果这两个条件都成立:

    • lookup是分配给Lookup 的前缀,

    • 查找有一个分配给name的值,

    查找的值被替换。否则, ${name}的扩展将被替换。

    如果name以连字符-开头(例如-variable ),则必须使用反斜杠\ (例如\-variable )进行转义。

    最常见的查找前缀是:

    上述扩展有一个带有附加default值的版本,如果查找失败,该默认值将被扩展

    ${name:-default}

    如果配置文件的Properties元素具有名为name,则其值将被替换。否则,将替换default扩展

     

    如果name包含:字符,则会按以下规则展开。

    ${lookup:name:-default}

    如果这两个条件都成立:

    • lookup是分配给Lookup 的前缀,

    • 查找有一个分配给name,

    查找的值被替换。否则,将替换${name:-default}的扩展。

    为了防止上述表达式之一的扩展,初始$必须加倍为$$ 。

    同样的规则适用于name参数:如果它包含${序列,则必须将其转义为$${ 。

    Properties:
      Property:
        - name: "FOO"
          value: "foo"
        - name: "BAR"
          value: "bar"

    并且OS环境变量FOO的值为environment ,Log4j将计算表达式如下

      表达   价值

    ${FOO}

    foo

    ${BAZ}

    ${BAZ}

    ${BAR:-${FOO}}

    bar

    ${BAZ:-${FOO}}

    foo

    ${env:FOO}

    environment

    ${env:BAR}

    bar

    ${env:BAZ}

    ${BAZ}

    ${env:BAR:-${FOO}}

    bar

    ${env:BAZ:-${FOO}}

    foo

    出于安全原因,如果${…​}表达式的扩展包含其他表达式,则这些表达式将不会被扩展。

    然而, Properties容器中定义的属性可以相互依赖。例如,如果您的配置包含:

    Properties:
      Property:
        - name: "logging.file"
          value: "${logging.dir}/app.log"
        - name: "logging.dir"
          value: "${env:APP_BASE}/logs"
        - name: "APP_BASE"
          value: "."

    logging.dir属性将在logging.file属性之前展开,并且展开的值将在${logging.dir}/app.log中替换。因此, logging.file属性的值为:

    • ./logs/app.log如果环境变量APP_BASE没有定义,

    • /var/lib/app/logs/app.log如果环境变量APP_BASE的值为/var/lib/app 。

     

    运行时属性替换

    对于大多数属性,属性替换仅在配置时执行一次,但此规则也有例外:当发生特定于组件的事件时也会评估某些属性。

      在这种情况下:

    • 如果您希望在配置时进行属性替换,请使用一个美元符号,例如${date:HH:mm:ss} 。

    • 如果您希望在运行时进行属性替换,请使用两个美元符号,例如$${date:HH:mm:ss}

    支持运行时属性替换的属性列表是:

    某些查找在运行时扩展时的行为可能会有所不同。有关详细信息,请参阅查找评估上下文

     

    这 的Route组件 路由附加器 完全是另一种情况。 其子级的属性在运行时扩展,但在配置时不扩展。

    Route组件内,不应使用转义的$${...}表达式,而只能使用未转义的${...}表达式。

     

    Routing:
      name: "ROUTING"
      Routes:
        pattern: "$${sd:type}" 
        Route:
          File:
            name: "ROUTING-${sd:type}" 
            fileName: "logs/${sd:type}.log" 
            JsonTemplateLayout:
              EventTemplateAdditionalField:
                name: "type"
                value: "${sd:type}" 
      pattern属性在配置时以及每次路由日志事件时进行评估。因此,美元$符号需要转义。
      Route元素的子元素的所有属性都有延迟评估。因此,他们只需要一个$符号。

     

    仲裁者Arbiters

    虽然属性替换允许在多个部署环境中使用相同的配置文件,但有时更改配置属性的值是不够的。

    仲裁器对于配置元素来说就像属性替换对于配置属性一样:它们允许有条件地将配置元素的子树添加到配置文件中。

    仲裁器可以出现在配置中允许元素并且可以嵌套的任何位置。 因此,仲裁器可以封装像单个属性声明或一整套附加器、记录器或其他仲裁器一样简单的东西。
    对于仲裁器的父元素,仲裁器的子元素必须是有效元素。

    有关可用仲裁器的完整列表,请参阅 插件参考。 在下面的示例中,我们将使用 默认仲裁者, 选择 和 系统属性仲裁器

    例如,您可能想在生产和开发环境中使用不同的布局:

     Appenders:
      File:
        name: "MAIN"
        fileName: "logs/app.log"
        SystemPropertyArbiter:
          - propertyName: "env"
            propertyValue: "dev"
            PatternLayout:
              pattern: "%d [%t] %p %c - %m%n"
          - propertyName: "env"
            propertyValue: "prod"
            JsonTemplateLayout: {}

    如果 Java 系统属性env的值为dev ,则将使用模式布局。
      如果 Java 系统属性env的值为prod ,则将使用 JSON 模板布局。

    上面的示例有一个问题:如果 Java 系统属性env值不同于devprod ,则追加器将没有布局。

    在这种情况下, Select插件很有用:此配置元素包含仲裁器列表和 DefaultArbiter元素。如果没有仲裁器匹配,则将使用DefaultArbiter元素中的配置:

    Select:
      SystemPropertyArbiter:
        propertyName: "env"
        propertyValue: "dev"
        PatternLayout: {}
      DefaultArbiter:
        JsonTemplateLayout: {}

      如果 Java 系统属性env的值为dev ,则将使用模式布局。
      否则,将使用 JSON 模板布局。

      复合配置CompositeConfiguration

    有时可能需要组合多种配置。例如,

    • 您有一个应始终存在的通用 Log4j 核心配置,以及一个特定于环境的配置,该配置根据应用程序运行的环境(测试、生产等)扩展通用配置。

    • 您开发一个框架,它包含预定义的 Log4j Core 配置。然而您希望允许用户在必要时扩展它。

    • 您从多个来源收集 Log4j Core 配置。

    您可以在log4j2.configurationFile配置属性中提供一系列以逗号分隔的文件路径或 URL,其中每个资源将被读入一个Configuration中,然后最终使用CompositeConfiguration合并为一个资源。

    Configuration properties

从 Log4j 2.10 开始,所有属性名称都遵循通用的命名方案:

log4j2.camelCasePropertyName

除了环境变量之外,它遵循以下内容:

LOG4J_CAMEL_CASE_PROPERTY_NAME

  习俗。

 

如果log4j2.system.properties文件在类路径上可用,则其内容将在 Log4j 启​​动时获取到 Java 系统属性中。

当应用程序的日志记录速度比底层追加器能够跟上足够长的时间来填充队列时,行为由AsyncQueueFullPolicy确定。

log4j2.asyncLoggerConfigWaitStrategy

指定 LMAX Disruptor 使用的WaitStrategy 。

该值必须是预定义常量之一:

  堵塞Block

对等待日志事件的 I/O 线程使用锁和条件变量的策略。当吞吐量和低延迟不像 CPU 资源那么重要时,可以使用块。建议用于资源受限/虚拟化环境。

  暂停Timeout

Block策略的一种变体,它将定期从锁定条件await()调用中唤醒。这确保了如果不知何故错过通知,消费者线程不会被卡住,而是会以较小的延迟延迟恢复(请参阅log4j2.asyncLoggerTimeout )

  睡觉Sleep

一种策略,首先旋转,然后使用Thread.yield() ,并最终在 I/O 线程等待日志事件时停放操作系统和 JVM 允许的最小纳秒数(请参阅log4j2.asyncLoggerRetrieslog4j2.asyncLoggerSleepTimeNs )。睡眠是性能和 CPU 资源之间的一个很好的折衷方案。此策略对应用程序线程的影响非常小,但会在实际记录消息时产生一些额外的延迟。

  屈服Yield

是一种将使用100% CPU 的策略,但如果其他线程需要 CPU 资源,则会放弃 CPU。

另请参阅自定义WaitStrategy以了解配置等待策略的替代方法。

如果JANSI库位于应用程序的运行时类路径上,则可以使用以下属性来控制其使用:

log4j2.skipJansi

  环境。多变的

LOG4J_SKIP_JANSI

  类型

boolean

  默认值

true

如果满足以下条件:

  •   Log4j 在 Windows 上运行,

  • 该属性设置为false ,

Log4j 将使用 JANSI 库为控制台附加器的输出着色。

线程上下文

可以使用以下属性微调ThreadContext类的行为。

 

这些配置属性仅由 Log4j API 的 Log4j Core 和Simple Logger实现使用。

log4j-to-slf4j日志记录桥将ThreadContext调用委托给SLF4J MDC

log4j-to-jul日志记录桥会忽略所有ThreadContext方法调用。

property-sources

财产来源

Log4j配置属性子系统合并了实现Java接口的多个属性源的内容 PropertySource 。

可以通过以下方式添加其他属性源类:

  • 标准 Java SE ServiceLoader 机制,

  • 以编程方式使用PropertiesUtiladdPropertySource()removePropertySource()静态方法。

每个属性源都可以定义自己的属性名称命名约定,尽管大多数属性源都支持该标准:

log4j2.camelCasePropertyName

  习俗。

属性可以被具有较低数字优先级的源覆盖(例如-100 在100 之前)。

Log4j提供了以下实现:

表 1. PropertySource 优先级和描述
  姓名   优先事项   命名约定   模块   描述

SpringPropertySource

-100

  标准

log4j-spring

将属性解析委托给 Spring Environment 。有关详细信息,请参阅Log4j Spring Boot 支持

SystemPropertiesPropertySource

0

  标准

log4j-api

使用 Java System Properties解析属性。

EnvironmentPropertySource

100

  风俗

log4j-api

使用环境变量解析属性。

警告:此属性源的命名约定与标准命名约定不同。属性名称以 LOG4J_ 为前缀,全部大写,单词均用下划线分隔。

PropertyFilePropertySource

200

  标准

log4j-api

使用类路径上找到的所有名为log4j2.component.properties的资源解析属性。该属性源应用于更改应用程序的默认值。

属性的运行时评估

以下配置属性也在运行时评估,因此可以包含转义的$${...}属性替换表达式。

表 1. 运行时评估的属性列表
  成分   范围   事件类型   评估背景

  HTTP 附加程序

Property/value

  记录事件

  全球的

  卡夫卡追加器

key

  记录事件

  全球的

  NoSQL 附加程序

KeyValuePair/value

  记录事件

  全球的

  属性重写策略

Property/value

  记录事件

  全球的

  路线容器

pattern

  记录事件

  记录事件

  滚动文件追加器

filePattern

  滚下

  全球的

可选的翻转操作

basePath

  滚下

  全球的

这 Route 的组成部分 路由附加器 很特殊:它的子项在运行时评估,但在配置时评估。在Route组件内,不应使用转义的$${...}属性替换表达式,而只能使用未转义的${...}属性替换表达式。

有关更多详细信息,请参阅运行时属性替换

JANSI

如果应用程序在 Windows 上运行并且 詹西图书馆 可用时,控制台附加器将使用 JANSI 来模拟 ANSI 序列支持。 可以通过设置禁用此模式 log4j2.skipJansi 配置属性为true 。

使用 JANSI 需要额外的运行时依赖项:runtimeOnly 'org.fusesource.jansi:jansi:1.18'

表 2. 控制台 Appender 配置属性
  属性   类型   默认值   描述

  必需的

name

String

 

附加器的名称。

  选修的

bufferSize

int

8192

尺寸 ByteBuffer 由附加程序内部使用。

有关更多详细信息,请参阅缓冲

direct

boolean

false

如果设置为true ,日志事件将直接写入 FileDescriptor.out 或者 FileDescriptor.err 。

此设置绕过System.outSystem.err的缓冲,并可能提供与文件追加器相当的性能。

 

如果其他日志记录后端或应用程序本身使用System.out/System.err ,则将其设置为true可能会导致交错输出。

此设置与 follow属性 和 詹西支持

follow

boolean

false

如果设置为true ,附加程序将通过以下方式尊重System.out (分别为System.err )的重新分配 System.setOut (分别。 System.setErr )。

否则,将使用配置时System.out (或System.err )的值。

此设置与 direct属性 和 詹西支持

ignoreExceptions

boolean

true

如果为false ,记录异常将被转发给记录语句的调用者。否则,他们将被忽略。

记录异常也始终记录到状态记录器

immediateFlush

boolean

true

如果设置为true ,则追加器将在每个日志事件后刷新其内部缓冲区和System.out/System.err流的缓冲区。

有关更多详细信息,请参阅缓冲

target

Target

SYSTEM_OUT

它指定要使用哪个标准输出流:

SYSTEM_OUT

它使用标准输出。

SYSTEM_ERR

它使用标准错误输出。

生产环境的典型配置可能如下所示

示例log4j2.xml的片段
<Console name="CONSOLE"
         direct="true"> 
  <JsonTemplateLayout/> 
</Console>
  通过将direct设置为true来提高性能。
  使用结构化布局。需要其他依赖项,请参阅JSON 模板布局

 

文件附加器

文件追加器将日志写入文件系统。它们可以进一步分为:

  单文件附加器

有关详细信息,请参阅文件附加程序

  滚动文件追加器

有关详细信息,请参阅滚动文件追加器

  数据库附加程序

附加程序将日志事件直接写入数据库。

  JDBC 附加器

将日志事件发送到 JDBC 驱动程序

  JPA 附加器

使用 Jakarta Persistence API 将日志事件传送到数据库

  NoSQL 附加程序

将日志事件存储到面向文档的数据库

有关详细信息,请参阅数据库附加程序

网络附加器

这些附加程序使用简单的网络协议将日志事件传输到远程主机。支持的网络协议有:

UDP
TCP

这些由Socket Appender处理。

HTTP

这是由HTTP Appender处理的。

SMTP

这是由SMTP Appender处理的。

有关详细信息,请参阅网络附加程序

  消息队列追加器

消息队列附加程序将日志事件转发到消息代理。支持以下系统:

  水槽附加器

将日志事件转发到 Apache Flume服务器。

  JMS 附加器

将日志事件转发到 雅加达消息 2.0经纪人。

  卡夫卡附加器

将日志事件转发到 阿帕奇卡夫卡服务器。

  ZeroMQ/JeroMQ 附加器

将日志事件转发到 ZeroMQ代理。

有关详细信息,请参阅消息队列附加程序

  Servlet 附加程序

Servlet 附加程序允许用户将所有日志记录调用转发到 ServletContext.log() 方法。

 

这 ServletContext.log(String, Throwable) 方法早于现代日志 API。通过使用 Servlet Appender,您通常无法通过日志级别或记录器名称来区分日志事件。

Servlet Appender 没有配置属性。

您可以通过在配置文件中声明Servlet类型的附加程序来使用它:

Servlet:
  name: "SERVLET"
  PatternLayout:
    pattern: "%m%n"
    alwaysWriteExceptions: false

runtimeOnly 'org.apache.logging.log4j:log4j-jakarta-web'

委派附加程序追加器 :: Apache Log4j --- Appenders :: Apache Log4j

委托附加程序旨在装饰其他附加程序:

  异步追加器

在专用线程上执行所有 I/O

  故障转移附加程序

提供备份附加程序,以防附加程序失败

  重写appender

在将日志事件传送到目标之前对其进行修改

  路由附加器

为每个日志事件动态选择不同的附加程序

有关详细信息,请参阅委派 Appender 。

  延伸追加器 :: Apache Log4j --- Appenders :: Apache Log4j

 扩展附加器

Log4j Core 提供了三种文件附加器实现:

File

File附加器使用 FileOutputStream 访问日志文件。

RandomAccessFile

RandomAccessFile Appender 使用 RandomAccessFile 访问日志文件。

MemoryMappedFile

MemoryMappedFile Appender 将日志文件映射到 MappedByteBuffer 。

该附加程序不需要进行系统调用来写入磁盘,而是可以简单地更改程序的本地内存,这会快几个数量级。

 

两个附加程序,即使来自不同的记录器上下文,也共享一个共同的 FileManager 如果它们使用相同值的fileName属性

共享FileManager可保证多个附加程序按顺序访问日志文件,但要求大多数剩余配置参数相同。

 

 常用配置

表 1. 常用配置属性
  属性   类型   默认值   描述

  必需的

fileName

Path

 

当前日志文件的路径 如果包含该文件的文件夹不存在,则会创建该文件夹。

name

String

 

附加器的名称。

  选修的

bufferSize

int

8192

尺寸 ByteBuffer 由附加程序内部使用。

有关更多详细信息,请参阅缓冲

ignoreExceptions

boolean

true

如果为false ,记录异常将被转发给记录语句的调用者。否则,他们将被忽略。

记录异常也始终记录到状态记录器

immediateFlush

boolean

true

如果设置为true ,追加器将在每个事件后刷新其内部缓冲区。

有关更多详细信息,请参阅缓冲

File配置

除了常见的配置选项外, File附加器还提供以下配置选项:

表 3. File配置属性
  属性   类型   默认值   描述

append

boolean

true

如果true ,日志文件将在以下位置打开 APPEND模式

在大多数系统上,这可以保证原子写入到文件末尾,即使该文件是由多个应用程序打开的。

bufferedIo

boolean

true

如果设置为true ,Log4j Core 将在内部缓冲区中格式化每个日志事件,然后将其发送到底层资源。

有关更多详细信息,请参阅缓冲

createOnDemand

  布尔值

false

附加程序按需创建文件。仅当日志事件通过所有过滤器并路由到此附加程序时,附加程序才会创建文件。默认为 false。

filePermissions

PosixFilePermissions

null

如果不为null ,则它指定要应用于每个创建的文件的 POSIX 文件权限。 权限必须以所使用的格式提供 PosixFilePermissions.fromString() ,例如rw-rw---- 。

底层文件系统应支持 POSIX 文件属性视图。

fileOwner

String

null

如果不为null ,则它指定要应用于每个创建的文件的文件所有者。

底层文件系统应支持文件 所有者 属性视图。

fileGroup

String

null

如果不为null ,则它指定要应用于每个创建的文件的文件组所有者。

底层文件系统应支持 POSIX 文件属性视图。

locking

boolean

false

如果true ,Log4j 将在每个日志事件处锁定日志文件。

请注意,此设置的效果取决于操作系统:某些系统(例如大多数 POSIX 操作系统)不提供强制锁定,而仅提供建议文件锁定。

此设置还会降低附加器的性能。

RandomAccessFile配置

除了常见的配置选项之外, RandomAccessFile Appender 还提供以下配置选项:

表 4. RollingRandomAccessFile配置属性
  属性   类型   默认值   描述

append

boolean

true

如果true ,追加器开始在文件末尾写入。

此设置不提供与 RollingFile追加器。日志文件不能同时被多个应用程序打开。

File追加器不同,此追加器始终使用大小为bufferSize的内部缓冲区。

MemoryMappedFile配置

除了常见的配置选项之外, MemoryMappedFile Appender 还提供以下配置选项:

表 5. RollingRandomAccessFile配置属性
  属性   类型   默认值   描述

append

boolean

true

如果true ,追加器开始在文件末尾写入。

此设置不提供与 RollingFile追加器。日志文件不能同时被多个应用程序打开。

regionLength

int

32 × 1024 × 1024

它指定内存映射日志文件缓冲区的大小(以字节为单位)。

与其他文件追加器不同,此追加器始终使用大小为regionLength的内存映射缓冲区作为其内部缓冲区。

📖 MemoryMappedFile插件参考

  滚动文件追加器

Log4j Core 提供了多个附加程序,允许归档当前日志文件并截断​​它。

 

滚动文件附加器支持许多配置选项。

如果您只对一些配置示例感兴趣,请参阅配置食谱部分。

  附加器

Log4j Core 提供了两个滚动文件附加器:

RollingFile

RollingFile Appender 使用 FileOutputStream 访问日志文件。

RollingRandomAccessFile

RollingRandomAccessFile Appender 使用 RandomAccessFile 访问日志文件。

 

两个附加程序,即使来自不同的记录器上下文,也共享一个共同的 RollingFileManager如果:

共享RollingFileManager可保证翻转仅发生一次,但要求大部分剩余配置参数相同。

表 1. 常用配置属性
  属性   类型   默认值   描述

  必需的

filePattern

Path

 

归档日志文件的模式。有关详细信息,请参阅转换模式

如果fileNamenull ,则文件模式也将用于当前文件。

name

String

 

附加器的名称。

  选修的

bufferSize

int

8192

尺寸 ByteBuffer 由附加程序内部使用。

有关更多详细信息,请参阅缓冲

bufferedIo

boolean

true

如果设置为true ,Log4j Core 将在内部缓冲区中格式化每个日志事件,然后将其发送到底层资源。

RandomAccessRollingFile Appender 始终启用内部缓冲区。

有关更多详细信息,请参阅缓冲

createOnDemand

  布尔值

false

附加程序按需创建文件。仅当日志事件通过所有过滤器并路由到此附加程序时,附加程序才会创建文件。默认为 false。

fileName

Path

null

当前日志文件的路径可以为null 。如果包含该文件的文件夹不存在,则会创建该文件夹。

 

该属性不应包含任何 运行时查找 也不是模式转换器。

要使用模式转换器,请参阅filePattern 。

filePermissions

PosixFilePermissions

null

如果不为null ,则它指定要应用于每个创建的文件的 POSIX 文件权限。 权限必须以所使用的格式提供 PosixFilePermissions.fromString() ,例如rw-rw---- 。

底层文件系统应支持 POSIX 文件属性视图。

fileOwner

String

null

如果不为null ,则它指定要应用于每个创建的文件的文件所有者。

底层文件系统应支持文件 所有者 属性视图。

fileGroup

String

null

如果不为null ,则它指定要应用于每个创建的文件的文件组所有者。

底层文件系统应支持 POSIX 文件属性视图。

ignoreExceptions

boolean

true

如果为false ,记录异常将被转发给记录语句的调用者。否则,他们将被忽略。

记录异常也始终记录到状态记录器

immediateFlush

boolean

true

如果设置为true ,追加器将在每个事件后刷新其内部缓冲区。

有关更多详细信息,请参阅缓冲

表 2. 常见嵌套元素
  类型   多重性   描述

Filter

  零或一

允许在格式化和发送日志事件之前对其进行过滤。

另请参阅附加程序过滤阶段

Layout

  零或一

  格式化日志事件。

有关详细信息,请参阅布局

TriggeringPolicy

  

确定何时归档当前日志文件。

有关详细信息,请参阅触发策略

RolloverStrategy

  零或一

确定翻转期间执行的操作。

有关详细信息,请参阅展期策略

  转换模式

filePattern属性接受以下模式说明符:

所有模式还接受格式说明符,例如%03i将索引打印为 3 位数字的零填充数字。

 

$${date:...}运行时查找和%d{...}转换模式并不等效:

  • $${date:...}格式化当前日期。

  • %d{...}格式化上次滚动的日期。

filePattern属性设置为

$${date:yyyy-MM}/%d{yyyy-MM-dd}.log

会将2024-06-30.log日志文件写入2024-07目录。考虑使用

%d{yyyy-MM}/%d{yyyy-MM-dd}.log

  反而。

RollingFile配置

除了常见的配置选项外, RollingFile Appender 还提供以下配置选项:

表 3. RollingFile配置属性
  属性   类型   默认值   描述

append

boolean

true

如果true ,日志文件将在以下位置打开 APPEND模式

在大多数系统上,这可以保证原子写入到文件末尾,即使该文件是由多个应用程序打开的。

locking

boolean

false

如果true ,Log4j 将在每个日志事件处锁定日志文件。

请注意,此设置的效果取决于操作系统:某些系统(例如大多数 POSIX 操作系统)不提供强制锁定,而仅提供建议文件锁定。

此设置还会降低附加器的性能。

RollingRandomAccessFile文件配置

除了常见的配置选项外, RollingRandomAccessFile Appender 还提供以下配置选项:

表 4. RollingRandomAccessFile配置属性
  属性   类型   默认值   描述

append

boolean

true

如果true ,追加器开始在文件末尾写入。

此设置不提供与 RollingFile追加器。日志文件不能同时被多个应用程序打开。

  触发策略

触发策略是 Log4j 插件,它实现了 TriggeringPolicy 接口并用于决定何时滚动当前日志文件。

  常见问题

触发策略通常根据两个参数决定滚动文件:

  • 当前文件的大小。

     

    虽然所有 JVM 都可以可靠地检查文件的大小,但 Log4j 仅在启动时和每次翻转时检查文件大小,并根据传递的日志大小推断所有文件大小更改。如果多个管理器写入同一个文件,则大小计算将关闭。

  • 当前文件的创建时间戳。

     

    并非所有文件系统都支持创建时间戳。 最值得注意的是,POSIX 没有指定这样的时间戳,因此 Log4j 使用的值可能取决于所使用的文件系统的类型和 JVM。
    如果创建时间戳不可用,则使用上次修改时间戳,该时间戳最多可能相差整个滚动周期。

    看 BasicFileAttributes.creationTime() 了解更多详情。

    第一次翻转后,Log4j 使用翻转的时间戳作为创建时间戳。

OnStartupTriggeringPolicy

OnStartupTriggeringPolicy策略在 JVM 的生命周期内仅导致一次翻转。如果日志文件是在当前 JVM 启动时间之前创建的并且达到或超过最小文件大小,则会发生翻转。

表 5. OnStartupTriggeringPolicy配置属性
  属性   类型   默认值   描述

minSize

long

1

文件必须滚动的最小大小。

 

OnStartupTriggeringPolicy使用 JMX 来检索 JVM 的启动时间。

在 JMX 缺失或禁用的平台上,例如 Android 或 Google App Engine 中, OnStartupTriggeringPolicy将在加载其类时使用时间戳。

CronTriggeringPolicy

CronTriggeringPolicy基于 CRON 表达式触发翻转。

 

这 filePattern Appender 的属性应该包含 %d{...}时间戳,否则目标文件将在每次翻转时被覆盖。

 

该策略由定时器控制,并且在处理日志事件时是异步的,因此上一个或下一个周期的日志事件可能会出现在日志文件的开头或结尾。

表 6. CronTriggeringPolicy配置属性
  属性   类型   默认值   描述

evaluateOnStartup

  布尔值

false

启动时,将根据当前文件的创建时间戳来评估 cron 表达式。如果在该时间和当前时间之间发生翻转,则文件将立即翻转。

schedule

CronExpression

0 0 0 * * ?

cron 表达式,使用与 石英调度程序

支持的字段按顺序排列:

  •   第二

  •   分钟

  •   小时

  •   一个月中的某一天

  •   

  •   星期几

看 CronExpression 有关格式的完整规范。

SizeBasedTriggeringPolicy

一旦文件达到指定大小, SizeBasedTriggeringPolicy就会导致翻转。

大小可以以字节为单位指定,并带有后缀 KB、MB、GB 或 TB,例如20MB 。尺寸。该大小还可以包含小数值,例如1.5 MB 。大小是使用 Java 根区域设置计算的,因此必须始终使用句点作为小数单位。

 

当与基于时间的触发策略相结合时, filePattern Appender 的属性应该包含 %i转换模式。否则,目标文件将在每次翻转时被覆盖。

表 7. SizeBasedTriggeringPolicy配置属性
  属性   类型   默认值   描述

size

FileSize

10 MB

当前日志文件的最大文件大小。一旦达到此大小,就会发生翻转。

该属性的语法为:

<number> [ " " ] [ <unit> ]

  IE:

  • 一个被认可的数字 NumberFormat

  • 后跟可选的空格

  • 后跟可选单位: k 、 kB 、 M 、 MB 、 G 、 GB 、 T 、 TB 。

TimeBasedTriggeringPolicy

filePattern中的最小时间单位更改值时, TimeBasedTriggeringPolicy会导致翻转。因此,展期可以发生在月末、周末、午夜、小时结束时等。

 

这 filePattern Appender 的属性应该包含 %d{...}时间戳,否则目标文件将在每次翻转时被覆盖。

可以使用多个%d模式。翻转频率时间单位将由最后%d模式确定。

表 8. TimeBasedTriggeringPolicy配置属性
  属性   类型   默认值   描述

interval

int

1

根据最后%d{...}模式中最具体的时间单位,应多久发生一次翻转。

modulate

boolean

false

如果为true ,则翻转将与interval时间单位的边界对齐。

例如,如果轮换频率为每小时,间隔为4 ,并且应用程序在3:14开始:

false

将在每天3:00 、 7:00 、 11:00 、 15:00 、 19:0023:00进行滚动。

true

将在每天0:00 、 4:00 、 8:00 、 12:00 、 16:0020:00进行滚动。

仅当interval大于1时才适用。

maxRandomDelay

int

0

指示随机延迟翻转的最大秒数。

此设置对于多个应用程序配置为同时滚动日志文件的服务器非常有用,并且可以跨时间分散这样做的负载。

 

当滚动频率为每日或更低时,滚动文件追加器将在服务器默认时区的午夜轮换文件。

您可以通过在%d模式中指定不同的时区来修改滚动时间。有关更多详细信息,请参阅date转换器语法

  多项政策

滚动文件追加器仅允许一个嵌套的触发策略元素。如果您希望使用多个策略,则需要将它们包装在Policies元素中。元素本身没有配置属性。

 

同时使用两种基于时间的触发策略( CronTriggeringPolicyTimeBasedTriggeringPolicy )的效果未定义。

例如,以下 XML 片段定义了滚动日志的策略:

  •   当 JVM 启动时。

  • 当日志大小达到 10 MB 时。

  •   当地时间午夜。

示例log4j2.xml的片段
<RollingFile name="FILE"
             fileName="app.log"
             filePattern="app.%d{yyyy-MM-dd}.%i.log">
  <JsonTemplateLayout/>
  <Policies>
    <OnStartupTriggeringPolicy/>
    <SizeBasedTriggeringPolicy/>
    <TimeBasedTriggeringPolicy/>
  </Policies>
</RollingFile>

  展期策略

翻转策略决定如何轮换或删除旧的存档文件以便为新的存档文件腾出位置。翻转期间执行的操作包括:

  • 以循环方式重命名文件。有关更多详细信息,请参阅递增文件索引

  • 归档日志文件的压缩。有关更多详细信息,请参阅压缩存档文件

  • 删除旧的存档日志文件。有关更多详细信息,请参阅Delete操作

  • 更改归档日志文件的 POSIX 权限。有关更多详细信息,请参阅PosixViewAttribute操作

有两种不同的现成可用的翻转策略:

DefaultRolloverStrategy

这是使用的默认策略,如果 fileName名配置属性 已指定。 它将当前日志文件存储在fileName指定的位置,将归档日志文件存储在 fileName 指定的位置 filePattern 。

DirectWriteRolloverStrategy

这是使用的默认策略,如果 fileName名配置属性 为null 。它将当前日志文件直接存储在filePattern指定的滚动位置中。

 

不鼓励在fileName配置属性中使用运行时查找,因为它会破坏DefaultRolloverStrategy的逻辑。使用DirectWriteRolloverStrategy代替,并在配置中省略fileName属性。

  常用配置

两种翻转策略都支持以下配置参数:

表 9. 常见翻转策略配置属性
  属性   类型   默认值   描述

compressionLevel

int

7

确定归档日志文件的压缩级别。

有关更多详细信息,请参阅压缩存档文件

tempCompressedFilePattern

Path

 

存储压缩文件的临时位置。

有关更多详细信息,请参阅压缩存档文件

stopCustomActionsOnError

boolean

true

如果为true ,则如果其中一项操作失败,自定义操作将停止。

表 10. 通用滚动策略嵌套元素
  类型   多重性   描述

Action

  零个或多个

除了文件旋转和压缩之外,在翻转时执行的其他操作。

有关更多详细信息,请参阅可选操作

DefaultRolloverStrategy配置

DefaultRolloverStrategy支持控制文件索引增量的附加属性。

表 11. DefaultRolloverStrategy配置属性
  属性   类型   默认值   描述

fileIndex

  枚举

max

确定确定当前日志文件的%i转换模式值的策略。

支持的值为:

min

使用min属性的值。

max

使用minmax属性之间的第一个未使用的整数。

nomax

使用不低于min属性值的第一个未使用的整数。

min

int

1

%i转换模式的最小值。

max

int

7

%i转换模式的最大值。

如果fileIndex设置为nomax忽略此属性。

DirectWriteRolloverStrategy配置

DirectWriteRolloverStrategy有一组减少的配置选项,用于增加文件索引

  • 最小文件索引始终为1 。

  • 递增策略始终为max 。

  • 可以使用以下配置属性来配置最大文件索引:

    表 12. DirectWriteRolloverStrategy配置属性
      属性   类型   默认值   描述

    maxFiles

    int

    7

    %i转换模式的最大值。

增加文件索引

在翻转事件期间,当前日志文件将移动到通过评估确定的位置 filePattern ,使用min作为%i转换模式的值。如果旧日志文件已存在于该位置,则滚动文件附加程序:

  1. 尝试使用fileIndex配置属性指定的策略来增加%i的值。

  2. 如果失败,它将删除最旧的日志文件并轮换剩余的日志文件以为新的日志存档腾出位置。

有以下三种策略可供选择:

min

使用min策略,最新的日志文件将具有索引min ,最旧的日志文件将具有索引max 。

 

这是传统 UNIX 工具和 Log4j 1 使用的日志轮转策略。它不是Log4j 2 的默认策略。

假设min="1"max="3"日志文件的轮换如下图所示:

Diagram
max

使用max策略,最旧的日志文件将具有索引min ,最新的日志文件将具有索引max 。

 

这是 Log4j 2 以来的默认策略。

假设min="1"max="3"日志文件的轮换如下图所示:

Diagram
nomax

使用nomax策略,不会删除任何文件,并且将从min开始为较新的存档文件分配递增的索引号。

Diagram

压缩存档文件

当当前日志文件被归档时,滚动文件追加器可以对其进行压缩。根据存档文件名的扩展名激活压缩。可以识别以下扩展名:

  扩大 支持compressionLevel   描述

.zip

ZIP 存档使用 放气 算法

.gz

使用 DEFLATE 算法的GZIP存档

.bz2依赖

BZip2算法

.deflate部门

放气算法

.pack200 dep

Pack200算法

.xz相关

  XZ算法

.zst dep

Z标准算法

如果设置了tempCompressedFilePattern属性,则当前日志文件:

  • 将被压缩并存储在tempCompressedFilePattern指定的位置

  • 然后它将被移动到filePattern指定的位置。

  部门

使用这些压缩算法需要额外的依赖项:

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-compress</artifactId>
  <version>1.27.1</version>
  <scope>runtime</scope>
</dependency>

.xz.zst扩展名需要额外的依赖项。 看 共享压缩文档 了解更多详情。

  可选操作

文件的轮换和压缩始终由滚动文件附加程序自动处理。从 Log4j 2.6 开始,可以手动配置其他操作。

Log4j Core 提供了两种开箱即用的操作:

常用动作配置

这两个操作都支持以下配置属性:

表 13. 常见操作配置属性
  属性   类型   默认值   描述

basePath

Path

 

它设置操作的基目录。该操作将仅限于该目录中的文件,并且所有路径都将相对于该目录。

  必需的

followLinks

boolean

false

如果设置为true ,操作将遵循符号链接。

 

将此值设置为true将允许操作访问basePath外部的文件,这可能会带来安全风险。

maxDepth

int

1

可访问的最大目录级别数。默认情况下,该操作不会递归到basePath的子目录中。

表 14. 常见操作嵌套元素
  类型   多重性   描述

PathCondition

  零个或多个

一组路径条件。该操作仅对条件返回true文件执行。

Delete操作

Delete操作将删除符合配置条件的旧日志文件。

 

此操作的有限版本会自动执行以增加文件索引

如果您使用%d模式,则仅需要显式Delete操作。

除了常见的配置选项之外, Delete操作还支持以下选项:

表 15. Delete操作配置属性
  属性   类型   默认值   描述

testMode

boolean

false

如果为true ,则不会删除任何文件,但将发出INFO级别的状态记录器消息。

表 16. Delete操作嵌套元素
  类型   多重性   描述

PathCondition

  零个或多个

如果存在,则仅对条件返回true文件执行该操作。

必需的,除非提供了ScriptCondition ,在这种情况下这些元素将被忽略。

PathSorter

  零或一

basePath中包含的文件提供排序顺序。

默认实现是 PathSortByModificationTime ,按修改时间戳升序对文件进行排序。

ScriptCondition

  零或一

如果存在,则忽略所有嵌套的PathCondition并执行提供的脚本以选择要删除的文件。

PosixViewAttribute操作

此操作允许修改存档日志文件的 POSIX 属性(所有者、组和权限)。

 

默认情况下,POSIX 属性继承自当前日志文件。

仅当归档日志文件必须具有不同的属性时才需要执行此操作。

除了常见的配置选项之外, PosixViewAttribute Action 还支持以下选项。

表 17. PosixViewAttribute操作配置属性
  属性   类型   默认值   描述

filePermissions

PosixFilePermissions

null

如果不为null ,则它指定要应用于每个创建的文件的 POSIX 文件权限。 权限必须以所使用的格式提供 PosixFilePermissions.fromString() ,例如rw-rw---- 。

底层文件系统应支持 POSIX 文件属性视图。

fileOwner

String

null

如果不为null ,则它指定要应用于每个创建的文件的文件所有者。

底层文件系统应支持文件 所有者 属性视图。

fileGroup

String

null

如果不为null ,则它指定要应用于每个创建的文件的文件组所有者。

底层文件系统应支持 POSIX 文件属性视图。

表 18. PosixViewAttribute Action 嵌套元素
  类型   多重性   描述

PathCondition

  一个或多个

用于选择要修改的文件的一组条件。

  路径条件

为了选择文件进行其他操作,Log4j 提供了以下路径条件:

IfAccumulatedFileSize

当对文件列表进行评估时,此条件将每个文件的大小与前面文件的大小相加。对于超过可配置阈值的文件,它返回true 。

 

此条件的结果取决于文件的排序顺序。有关详细信息,请参阅PathSorter 。

表 19. IfAccumulatedFileSize配置属性
  属性   类型   默认值   描述

exceeds

FileSize

 

条件匹配的阈值大小。

承认与SizeBasedTriggeringPolicysize属性相同的语法。

  必需的

表 20. IfAccumulatedFileSize嵌套元素
  类型   多重性   描述

PathCondition

  零个或多个

一组可选的嵌套条件。仅当所有嵌套条件也匹配时,此条件才匹配。

IfAccumulatedFileCount

当对文件列表进行评估时,如果文件从 1 开始的索引超过可配置的阈值,则此条件返回true 。

 

此条件的结果取决于文件的排序顺序。有关详细信息,请参阅PathSorter 。

表 21. IfAccumulatedFileCount配置属性
  属性   类型   默认值   描述

exceeds

int

 

条件匹配的阈值。

  必需的

表 22. IfAccumulatedFileCount嵌套元素
  类型   多重性   描述

PathCondition

  零个或多个

一组可选的嵌套条件。仅当所有嵌套条件也匹配时,此条件才匹配。

IfFileName

根据文件对于基目录的路径来匹配文件。

表 23. IfFileName配置属性
  属性   类型   默认值   描述

glob

  细绳

 

使用glob模式匹配相对于基目录的路径。

看 FileSystem.getPathMatcher() 了解支持的glob语法。

必需的,除非指定了regex表达式。

regex

Pattern

 

使用正则表达式匹配相对于目录的路径。

看 Pattern 了解支持的正则表达式语法。

必需的,除非指定了glob 。

表 24. IfFileName嵌套元素
  类型   多重性   描述

PathCondition

  零个或多个

一组可选的嵌套条件。仅当所有嵌套条件也匹配时,此条件才匹配。

IfLastModified

根据文件的上次修改时间戳接受文件。

表 25. IfLastModified配置属性
  属性   类型   默认值   描述

age

Duration

 

该条件接受与指定持续时间一样旧或早于指定持续时间的文件。

  必需的

表 26. IfLastModified嵌套元素
  类型   多重性   描述

PathCondition

  零个或多个

一组可选的嵌套条件。仅当所有嵌套条件也匹配时,此条件才匹配。

IfNot

否定嵌套条件的结果。

表 27. IfNot嵌套元素
  类型   多重性   描述

PathCondition

  

要否定的路径条件。

IfAll

如果所有嵌套条件都为真,则接受文件。

表 28. IfAll嵌套元素
  类型   多重性   描述

PathCondition

  一个或多个

要检查的嵌套条件。

IfAny

表 29. IfAny嵌套元素
  类型   多重性   描述

PathCondition

  一个或多个

要检查的嵌套条件。

ScriptCondition

ScriptCondition使用 JSR 223 脚本来确定匹配文件的列表。

它的配置由单个嵌套脚本元素组成:

表 30. ScriptCondition嵌套元素
  类型   多重性   描述

Script , ScriptFile 或者 ScriptRef

  

对要执行的脚本的引用。

有关脚本编写的更多详细信息,请参阅脚本

该脚本必须返回一个列表 PathWithAttributes 对象并支持以下绑定:

表 31. 脚本绑定
  绑定名称   类型   描述

basePath

Path

基本目录的路径。

configuration

Configuration

Configuration对象。

pathList

List<PathWithAttributes>

基本目录中包含的文件列表。

路径是通过以下方式获得的 解决 相对文件名 basePath 。

substitutor

StrSubstitutor

StrSubstitutor用于替换查找变量。

statusLogger

Logger

脚本中的诊断消息使用的状态记录器

<key>

String

如果<key>不是上述之一,它将绑定到给定的值 全局Properties配置元素。

有关ScriptCondition用法的示例,请参阅下面的使用ScriptCondition示例。

  配置配方

logrotate等效配置

Logrotate是一种常见的 UNIX 实用程序,用于轮换日志文件。

由于无法通知 Java 应用程序需要重新加载日志文件,因此可以通过其copytruncate选项将logrotate与 Java 应用程序一起使用(请参阅 logrotate(8)手册页)。因此,示例logrotate配置文件可能如下所示:

/var/log/app.log {
  copytruncate
  compress
  rotate 15
  daily
  maxsize 100k
}

这个配置有一个问题, copytruncate选项的文档中有解释:

请注意,复制文件和截断文件之间的时间片非常短,因此可能会丢失一些日志记录数据。

幸运的是,您可以使用滚动文件附加程序来替换logrotate的使用。等效的配置如下所示:

示例log4j2.xml的片段
<RollingFile name="FILE"
             fileName="/var/log/app.log"
             filePattern="/var/log/app.log.%i.gz"> 
  <JsonTemplateLayout/>
  <DefaultRolloverStrategy max="15"/> 
  <Policies>
    <CronTriggeringPolicy schedule="0 0 0 * * ?"/> 
    <SizeBasedTriggeringPolicy size="100k"/> 
  </Policies>
</RollingFile>
  相当于compress :压缩存档文件。
  相当于rotate 15 :只保留最新的15日志文件。
  相当于daily :日志将在每天的午夜轮换。
  相当于maxsize 100k :如果日志大小超过 100 kB,则日志将被轮换。

带时间戳的日志文件名

以下配置每天创建一个日志文件并删除超过 15 天的日志文件。

 

由于我们在配置文件中有一个%d模式,因此我们需要使用显式的Delete操作

示例log4j2.xml的片段
<RollingFile name="FILE"
             filePattern="/var/log/app.%d{yyyy-MM-dd}.log.gz"> 
  <JsonTemplateLayout/>
  <DirectWriteRolloverStrategy>
    <Delete basePath="/var/log"> 
      <IfFileName regex="app\.\d{4}-\d{2}-\d{2}\.log\.gz"/> 
      <IfLastModified age="P15D"/>
    </Delete>
  </DirectWriteRolloverStrategy>
  <TimeBasedTriggeringPolicy/>
</RollingFile>
  由于我们使用DirectWriteRolloverStrategy ,因此仅使用filePattern属性。
  提供了显式Delete操作。
  您可以使用正则表达式或更简单的app.*.log.gz glob 模式选择要删除的文件。

使用ScriptCondition

如果提供的路径条件不充分,您可以将ScriptCondition与任意脚本一起使用。

上面的示例可以重写为以下 Groovy 脚本:

def limit = FileTime.from(ZonedDateTime.now().minusDays(15).toInstant())
def matcher = FileSystems.getDefault().getPathMatcher('glob:app.*.log.gz')
statusLogger.info("Deleting files older than {}.", limit) 
return pathList.stream()
        .filter({
            def relPath = basePath.relativize(it.path) 
            def lastModified = it.attributes.lastModifiedTime()
            Files.isRegularFile(it.path)
                    && lastModified <= limit 
                    && matcher.matches(relPath) 
        })
        .collect(Collectors.toList())
  在脚本中添加状态记录器调用可能有助于调试它。
  PathWithAttributes.getPath() 总是以basePath开头,所以我们需要将其相对化。
  相当于IfLastModified条件。
  相当于IfFileName条件。

您可以在配置文件中使用该脚本,如下所示:

示例log4j2.xml的片段
<RollingFile name="FILE"
             filePattern="/var/log/app.%d{yyyy-MM-dd}.log.gz">
  <JsonTemplateLayout/>
  <DirectWriteRolloverStrategy>
    <Delete basePath="/var/log">
      <ScriptCondition>
        <ScriptFile path="script-condition.groovy"
                    language="groovy"/>
      </ScriptCondition>
    </Delete>
  </DirectWriteRolloverStrategy>
  <TimeBasedTriggeringPolicy/>
</RollingFile>

每月单独的文件夹

我们还可以为临时相关的文件创建单独的文件夹。在下面的示例中,我们为每个月创建一个不同的文件夹:

示例log4j2.xml的片段
<RollingFile name="FILE"
             filePattern="/var/log/app/%d{yyyy-MM}/%d{yyyy-MM-dd}.log.gz"> 
  <JsonTemplateLayout/>
  <DirectWriteRolloverStrategy>
    <Delete basePath="/var/log/app"
            maxDepth="2"> 
      <IfLastModified age="P90D"/>
    </Delete>
  </DirectWriteRolloverStrategy>
  <TimeBasedTriggeringPolicy/>
</RollingFile>
  我们使用两个%d模式来指定文件夹和文件名。
  我们增加了Delete操作的递归深度以扩展到基本目录的子文件夹。

 

 

 

posted @ 2025-01-16 14:10  CharyGao  阅读(408)  评论(0)    收藏  举报