Tomcat logback滚动日志后丢失堆栈追踪,求单日志整合归档方案
问题背景
- 部署Java WAR包到
/tomcat/webapps,通过/tomcat/bin/startup.sh启动Tomcat - 默认
/tomcat/logs/catalina.out整合了Tomcat控制台与应用日志 - 尝试用
/tomcat/lib下的logback.xml实现日志滚动时,出现滚动后丢失错误堆栈的问题:- 初始配置仅用RollingFileAppender指向catalina.out,滚动后触发错误只显示ERROR信息,无堆栈,重启后恢复
- 添加ConsoleAppender指向System.err后,初始日志重复,第一次滚动后正常,第二次滚动又丢失堆栈
- 改输出到mycatalina.log后,堆栈仅写入catalina.out,日志拆分后难以匹配关联条目
- 用logrotate需为30+Tomcat实例配置crontab,维护成本过高
- 核心需求:将Tomcat控制台输出、应用日志整合至单个文件,并实现自动归档
方案一:用Logback正确配置实现整合与滚动
之前的问题核心是:Tomcat的catalina.out是JVM的System.out/System.err重定向文件,Logback直接写入该文件会与Tomcat的输出流产生句柄冲突,滚动后文件引用不一致导致堆栈丢失。正确做法是让Logback接管所有日志(包括Tomcat自身控制台输出),不再共用catalina.out。
完整Logback配置(/tomcat/lib/logback.xml)
<configuration scan="true" debug="false"> <!-- 定义日志根目录与归档目录 --> <property name="LOG_HOME" value="${catalina.base}/logs"/> <property name="ARCHIVE_HOME" value="${LOG_HOME}/archived"/> <!-- 统一日志滚动Appender:整合所有日志 --> <appender name="UNIFIED_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/unified.log</file> <!-- 小时级滚动,保留30天归档(720小时) --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${ARCHIVE_HOME}/%d{yyyy-MM-dd-HH}_unified.log.gz</fileNamePattern> <maxHistory>720</maxHistory> <cleanHistoryOnStart>true</cleanHistoryOnStart> </rollingPolicy> <!-- 编码器:确保完整输出堆栈信息 --> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 配置Tomcat核心组件的Logger,避免重复输出 --> <logger name="org.apache.catalina" level="INFO" additivity="false"> <appender-ref ref="UNIFIED_LOG"/> </logger> <logger name="org.apache.tomcat" level="INFO" additivity="false"> <appender-ref ref="UNIFIED_LOG"/> </logger> <logger name="org.apache.jasper" level="INFO" additivity="false"> <appender-ref ref="UNIFIED_LOG"/> </logger> <!-- 根Logger:接管所有应用日志 --> <root level="INFO"> <appender-ref ref="UNIFIED_LOG"/> </root> </configuration>
配套修改:关闭Tomcat默认的catalina.out重定向
编辑/tomcat/bin/catalina.sh,注释或删除原有的输出重定向逻辑,避免Tomcat生成冗余的catalina.out:
# 注释掉原重定向启动代码 # touch "$CATALINA_OUT" # "$_RUNJAVA" $LOGGING_CONFIG $JAVA_OPTS $CATALINA_OPTS \ # -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ # -Dcatalina.base="$CATALINA_BASE" \ # -Dcatalina.home="$CATALINA_HOME" \ # -Djava.io.tmpdir="$CATALINA_TMPDIR" \ # org.apache.catalina.startup.Bootstrap "$@" start \ # >> "$CATALINA_OUT" 2>&1 & # 替换为直接启动(Logback已接管所有日志输出) "$_RUNJAVA" $LOGGING_CONFIG $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ org.apache.catalina.startup.Bootstrap "$@" start
方案二:用Tomcat自带的logging.properties实现catalina.out滚动
如果不想引入Logback,可修改Tomcat默认的/tomcat/conf/logging.properties,通过org.apache.juli.FileHandler实现catalina.out的自动滚动与日志整合:
修改后的logging.properties配置
# 配置日志处理器:仅保留文件输出,关闭控制台输出避免重复 handlers = 1catalina.org.apache.juli.FileHandler # 配置catalina.out的滚动规则 1catalina.org.apache.juli.FileHandler.level = FINE 1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 1catalina.org.apache.juli.FileHandler.prefix = catalina. # 开启滚动,按小时分割,保留30天归档 1catalina.org.apache.juli.FileHandler.maxDays = 30 1catalina.org.apache.juli.FileHandler.formatter = org.apache.juli.VerbatimFormatter 1catalina.org.apache.juli.FileHandler.rotatable = true 1catalina.org.apache.juli.FileHandler.suffix = .yyyy-MM-dd-HH.log # 配置Tomcat核心组件与应用日志统一输出到catalina的Handler org.apache.catalina.level = INFO org.apache.catalina.handlers = 1catalina.org.apache.juli.FileHandler org.apache.tomcat.level = INFO org.apache.tomcat.handlers = 1catalina.org.apache.juli.FileHandler org.apache.jasper.level = INFO org.apache.jasper.handlers = 1catalina.org.apache.juli.FileHandler # 根Logger配置:所有日志统一输出 .level = INFO .handlers = 1catalina.org.apache.juli.FileHandler
说明
- Tomcat的
org.apache.juli.FileHandler原生支持按时间滚动,suffix配置为小时级即可实现每小时归档 maxDays自动清理过期日志,无需额外维护crontab- 每个Tomcat实例独立配置,新增实例时直接复制配置文件即可
内容的提问来源于stack exchange,提问作者KamilSz
相关产品推荐
相关产品推荐

