如何在Tomcat 9中捕获后台运行的专有产品的System.out.println输出并实现日志轮转与过滤
首先得说,你遇到的这个场景挺常见的——专有程序硬写System.out又不让改代码,还得把这些输出管起来。别担心,咱们可以利用Tomcat自带的日志工具或者Log4j2来解决,完全不需要碰业务代码。
方案一:用Tomcat自带的Juli日志系统(零额外依赖)
Tomcat默认会把System.out/System.err的输出接管到它的Juli日志框架里,咱们只要配置一下就能实现轮转和基本过滤。
步骤1:修改Tomcat的logging.properties
打开Tomcat安装目录下的conf/logging.properties,按下面的步骤改:
添加自定义的System.out文件处理器:
在文件末尾加一段配置,定义专门存System.out输出的日志文件,同时开启轮转:# 自定义System.out专属日志处理器 systemout.org.apache.juli.FileHandler.level = INFO systemout.org.apache.juli.FileHandler.directory = ${catalina.base}/logs systemout.org.apache.juli.FileHandler.prefix = systemout. # 开启日志轮转 systemout.org.apache.juli.FileHandler.rotatable = true # 日志保留90天,过期自动删除 systemout.org.apache.juli.FileHandler.maxDays = 90 # 单个日志文件最大100MB,超过就轮转 systemout.org.apache.juli.FileHandler.maxFileSize = 100MB # 缓冲区大小,优化写入性能 systemout.org.apache.juli.FileHandler.bufferSize = 16384 # 日志格式,加上调用类信息方便过滤 systemout.org.apache.juli.FileHandler.formatter = org.apache.juli.OneLineFormatter systemout.org.apache.juli.FileHandler.formatter.format = %d{yyyy-MM-dd HH:mm:ss} %level [%thread] %logger - %msg (%caller{1})%n把System.out绑定到这个处理器:
找到文件里的logger配置部分,添加:# 让System.out的输出只走咱们定义的处理器 java.lang.System.level = INFO java.lang.System.handlers = systemout.org.apache.juli.FileHandler # 禁止输出到父处理器(避免重复打日志) java.lang.System.useParentHandlers = false
步骤2:实现类/包过滤
因为System.out本身不带类名信息,咱们刚才在日志格式里加了%caller{1},它会显示调用println()的类和方法(比如com.foo.MyClass.main(MyClass.java:123))。之后你可以用Windows的findstr或者第三方日志工具(比如LogParser)来筛选特定类的日志:
# 筛选com.foo包下的类的System.out输出 findstr /i "com.foo" logs\systemout.2024-05-20.log
方案二:用Log4j2实现更灵活的过滤和轮转(推荐)
如果需要更精细的过滤规则(比如直接在配置里过滤特定类),可以用Log4j2来桥接System.out,它的功能更强大。
步骤1:准备Log4j2依赖
确保Tomcat的lib目录下有这些jar包:
- log4j-api.jar
- log4j-core.jar
- log4j-jul.jar(用来把JUL的日志桥接到Log4j2,Tomcat的
System.out会通过JUL流转过来)
步骤2:配置webapp里的log4j2.properties
打开webapp的WEB-INF/classes/log4j2.properties,添加以下配置:
# 全局配置 status = warn name = PropertiesConfig # 定义System.out的滚动文件Appender appender.systemout.type = RollingFile appender.systemout.name = SystemOutAppender appender.systemout.fileName = ${sys:catalina.base}/logs/systemout.log # 轮转后的文件名格式(按天+序号压缩) appender.systemout.filePattern = ${sys:catalina.base}/logs/systemout-%d{yyyy-MM-dd}-%i.log.gz # 日志格式,包含调用类信息 appender.systemout.layout.type = PatternLayout appender.systemout.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg (%C{1}.%M:%L)%n # 轮转规则:每天转一次,单个文件超过100MB也转 appender.systemout.policies.type = Policies appender.systemout.policies.time.type = TimeBasedTriggeringPolicy appender.systemout.policies.time.interval = 1 appender.systemout.policies.time.modulate = true appender.systemout.policies.size.type = SizeBasedTriggeringPolicy appender.systemout.policies.size.size = 100MB # 保留最近30天的日志 appender.systemout.strategy.type = DefaultRolloverStrategy appender.systemout.strategy.max = 30 # 添加类过滤规则:只保留com.foo包下的类的输出 appender.systemout.filter.type = RegexFilter appender.systemout.filter.regex = com\\.foo\\. appender.systemout.filter.onMatch = ACCEPT appender.systemout.filter.onMismatch = DENY # 把System.out绑定到这个Appender logger.systemout.name = System.out logger.systemout.level = INFO logger.systemout.additivity = false logger.systemout.appenderRef.systemout.ref = SystemOutAppender # 可选:处理System.err的输出 appender.systemerr.type = RollingFile appender.systemerr.name = SystemErrAppender appender.systemerr.fileName = ${sys:catalina.base}/logs/systemerr.log appender.systemerr.filePattern = ${sys:catalina.base}/logs/systemerr-%d{yyyy-MM-dd}-%i.log.gz appender.systemerr.layout.type = PatternLayout appender.systemerr.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg (%C{1}.%M:%L)%n appender.systemerr.policies.type = Policies appender.systemerr.policies.time.type = TimeBasedTriggeringPolicy appender.systemerr.policies.time.interval = 1 appender.systemerr.policies.time.modulate = true appender.systemerr.policies.size.type = SizeBasedTriggeringPolicy appender.systemerr.policies.size.size = 100MB appender.systemerr.strategy.type = DefaultRolloverStrategy appender.systemerr.strategy.max = 30 logger.systemerr.name = System.err logger.systemerr.level = ERROR logger.systemerr.additivity = false logger.systemerr.appenderRef.systemerr.ref = SystemErrAppender
步骤3:验证配置
重启Tomcat后,你会看到logs/systemout.log生成,里面只有com.foo包下的类的System.out输出,并且会自动按天/大小轮转,超过30天的日志会被清理。
注意事项
- 如果你用方案一,记得重启Tomcat让配置生效;
- 方案二里的Log4j2版本要和Tomcat兼容(Tomcat 9推荐用Log4j2 2.x系列);
- 如果你没法往Tomcat的
lib目录加jar包,可以把Log4j2的jar包放到webapp的WEB-INF/lib下,同样生效。
备注:内容来源于stack exchange,提问作者Zlelik

