记录失败时停止Spring Boot应用程序log4j2

问题描述

我有一个标准的Spring Boot微服务,该服务使用Log4j2进行所有日志记录。

在日志记录失败(例如磁盘已满)的情况下,我想正常关闭Spring Boot应用程序。有没有办法设置它?

参考: How to handle disk full errors while logging in logback?->此问题未回答此特定问题。 https://logging.apache.org/log4j/2.x/manual/appenders.html#FailoverAppender->阅读FailoverAppender的文档,我不确定这是否符合要求。

解决方法

我猜您需要做的是通过其他机制来检测剩余磁盘,而不是依靠记录器来检测它。 Spring Boot执行器用于提供许多指标,包括内存和磁盘。它使外部服务可以通过HTTP端点从正在运行的实例中获取指标。

在这里查看执行器如何获取磁盘空间:https://github.com/spring-projects/spring-boot/blob/v2.3.3.RELEASE/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java 说明该机制很简单:

@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
    long diskFreeInBytes = this.path.getUsableSpace();
    if (diskFreeInBytes >= this.threshold.toBytes()) {
        builder.up();
    }
    else {
        logger.warn(LogMessage.format("Free disk space below threshold. Available: %d bytes (threshold: %s)",diskFreeInBytes,this.threshold));
        builder.down();
    }
    builder.withDetail("total",this.path.getTotalSpace()).withDetail("free",diskFreeInBytes)
            .withDetail("threshold",this.threshold.toBytes()).withDetail("exists",this.path.exists());
}

在主应用程序类中,您可以使用与上述相同的机制,定期检查磁盘空间并在磁盘空间低于特定阈值时关闭应用程序上下文。关闭应用程序可以是这样的:

int exitCode = SpringApplication.exit(ctx,new ExitCodeGenerator() {
@Override
public int getExitCode() {
        // return the error code
        return 0;
    }
});

 
System.exit(exitCode);
,

每个Appender都由Log4j 2中的AppenderControl包装。AppenderControl捕获Appender引发的所有异常。它的默认行为是将错误处理程序附加到Appender并调用其error()方法。当该方法返回时,它将检查属性“ ignoreExceptions”是否为false(默认为true),如果是,它将重新引发异常。

理想情况下,您可以实现自己的错误处理程序,但编写该代码(我)的人忘了使其可配置。参见https://issues.apache.org/jira/browse/LOG4J2-2927

FailoverAppender设计为包装一个附加程序,然后在发生错误时故障转移到另一个附加程序。这要求在目标附加程序上将ignoreExceptions设置为false。您可以复制FailoverAppender来创建自己的自定义附加程序,而在发生错误时执行关闭操作。

,

Spring Boot带有一个执行器,该执行器具有(默认情况下是禁用的)shutdown端点。 基本上,启用此端点后,便可以通过JMX或HTTP关闭spring boot应用程序。到目前为止,这是我知道正常关闭Spring Boot应用程序的最佳方法。

此端点的源代码可用here,您可以很容易地看到它毕竟在应用程序上下文中调用了close方法。

由于您的问题,我从未使用过log4j2,但是作为一个想法,您可以创建自己的附加程序,该附加程序将包装分配给记录器的真实附加程序,它可以捕获异常并调用{{1} }方法(在这种情况下,它还必须维护对应用程序上下文的引用)或例如通过JMX调用close端点。这可以“补充” @rgoers提供的答案,后者从log4j2的角度解决了该问题。