You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Logback在Docker容器退出时抛出ConcurrentModificationException问题求助

问题:Docker容器内Logback退出时抛出ConcurrentModificationException异常

应用作为独立Java程序运行时正常,但在Docker容器内退出时触发ConcurrentModificationException异常,堆栈跟踪如下:

Exception in thread "Logback shutdown hook [default]" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
    at ch.qos.logback.classic.LoggerContext.fireOnReset(LoggerContext.java:323)
    at ch.qos.logback.classic.LoggerContext.reset(LoggerContext.java:226)
    at ch.qos.logback.classic.LoggerContext.stop(LoggerContext.java:348)
    at ch.qos.logback.core.hook.ShutdownHookBase.stop(ShutdownHookBase.java:39)
    at ch.qos.logback.core.hook.DelayingShutdownHook.run(DelayingShutdownHook.java:57)
    at java.base/java.lang.Thread.run(Thread.java:834)

Logback配置(logback-spring.xml)

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <!--See https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html -->
    <shutdownHook
            class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.contrib.json.classic.JsonLayout">
                <jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
                    <!-- <prettyPrint>true</prettyPrint> -->
                </jsonFormatter>
                <timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSXXX</timestampFormat>
                <includeContextName>false</includeContextName>
                <appendLineSeparator>true</appendLineSeparator>
            </layout>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>5000</queueSize> <!-- Default is 256 -->
        <discardingThreshold>0</discardingThreshold> <!-- Instruction to drop events
            once the buffer reaches the max capacity. Default value is 20%.
            20 means drop lower event when has 20% capacity remaining -->
        <neverBlock>true</neverBlock><!-- default false. Setting it to true to
            cause the Appender not block the application and just drop the messages -->
        <maxFlushTime>5000</maxFlushTime> <!-- The default maximum queue flush time allowed during appender stop. If
            the worker takes longer than this time it will exit, discarding any remaining
            cartItems in the queue -->
        <appender-ref ref="CONSOLE_JSON"/>
    </appender>

    <logger name="org.springframework.web" level="INFO"/>

    <root level="INFO">
        <appender-ref ref="ASYNCSTDOUT"/>
    </root>

</configuration>

POM依赖构件

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <dependencies>
      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.5.8</version>
        </dependency>
     
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.14</version>
        </dependency>
       
    
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>6.6</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback.contrib</groupId>
            <artifactId>logback-jackson</artifactId>
            <version>0.1.5</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback.contrib</groupId>
            <artifactId>logback-json-classic</artifactId>
            <version>0.1.5</version>
        </dependency>
        
    </dependencies>
解决方案

问题根源

异常发生在Logback关闭钩子遍历LoggerContext的监听器列表时,由于多线程并发修改非线程安全的ArrayList导致。Docker环境下JVM关闭流程与本地存在差异,触发了监听器在遍历过程中被修改的时序问题,而本地环境未复现该场景。

具体修复步骤

  1. 升级Logback版本
    Logback 1.2.3存在监听器列表并发修改的bug,1.2.9及以上版本已修复该问题。将pom中Logback相关依赖版本升级到1.2.10(与Spring Boot 2.5.8兼容):

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.10</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.10</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>1.2.10</version>
    </dependency>
    
  2. 调整Logback关闭钩子配置
    若暂时无法升级版本,可尝试两种调整方式:

    • 移除自定义DelayingShutdownHook,使用Spring Boot默认的关闭处理逻辑:
      <!-- 注释或删除该行 -->
      <!-- <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/> -->
      
    • 增加关闭延迟时间,确保异步日志队列完全刷写完成后再执行关闭:
      <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook">
          <delay>10000</delay> <!-- 设置10秒延迟 -->
      </shutdownHook>
      
  3. 升级Spring Cloud Sleuth版本
    当前使用的Sleuth 2.1.0与Spring Boot 2.5.8版本不匹配(对应Spring Cloud版本应为2020.0.x),升级到兼容版本可避免关闭时修改Logback监听器:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-sleuth</artifactId>
        <version>3.0.4</version>
    </dependency>
    

验证

修改后重新构建Docker镜像,运行容器并正常退出,检查日志是否不再出现ConcurrentModificationException异常。

内容的提问来源于stack exchange,提问作者Amol Aggarwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 01:50:30