logback对于过长的堆栈有什么处理方法?
项目上发生几次由于代码不当,无限递归引发Java栈溢出。即java.lang.StackOverflowError: null
现在的问题是,在logback中输出这样的异常信息时,logger.error("xxx", ex);
完整的异常堆栈输出,导致日志巨大,有的日志文件 几百兆,夸张的日志文件高达 10个G
运行环境:
- Java8 ,JVM主要参数:
-Xms512m -Xmx3072m -XX:MetaspaceSize=256m -XX:+UseG1GC -XX:-OmitStackTraceInFastThrow
- 尝试设置过:
-XX:MaxJavaStackTraceDepth=1024
只对最后一段caused by的堆栈深度有作用,对于几百兆的日志文件大小影响很小。 - 未指定 -Xss参数指定java栈大小,沿用缺省值
- logback 日志样式有限定 最大msg长度:%.-40000msg (实测该长度只影响 msg的最大长度,对堆栈长度无效)
- logback 使用的 异步的循环日志,每个日志文件最大限定 5M字节
所以,我的问题是:
- logback有没有什么参数设定,对这种超长的堆栈输出,进行优化输出的?
- 缘何StackOverflowError输出的日志文件大小差异这么大?
比如下面的试验代码,模拟堆栈溢出时,日志文件很小,只有几百K。
附:测试代码:
public static void main(String[] args) throws Throwable { LoggerFactory.getLogger(TestStackOverflow.class).error("开始测试堆栈溢出--日志大小--");
AtomicLong count = new AtomicLong(0);
try {
foo(count);
}
catch (Throwable e) {
LoggerFactory.getLogger(TestStackOverflow.class).error("结束测试堆栈溢出:循环次数:" + count);
LoggerFactory.getLogger(TestStackOverflow.class).error("", e);
}
}
private static void foo(AtomicLong count) throws Throwable {
count.incrementAndGet();
foo(count);
}
补充:
已采纳 @乔治 的答案。但需要做部分改进:
样例代码原理上有效,但实际对日志减小有限。 因为出现 StackOverflow时,同时往往伴有大量的 caused by,样例代码只精简掉了第一层的栈的层数。
接下来,可能要考虑下,是递归对 throwableProxy.getCause() 进行精简?
还是 要精简掉 caused by 的次数呢? 主要看哪种精简方式不会丢失关键信息,更有利于后期问题排查。
回答:
你可以在logback配置文件中添加一个自定义的过滤器来限制堆栈跟踪的长度。比如限制了堆栈跟踪的最大长度为200行:先创建一个自定义的过滤器类:
//TruncateStackTraceFilter.java:import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
public class TruncateStackTraceFilter extends Filter {
private static final int MAX_STACK_TRACE_LENGTH = 200;
@Override
public FilterReply decide(Object eventObject) {
IThrowableProxy throwableProxy = ((ch.qos.logback.classic.spi.LoggingEvent) eventObject).getThrowableProxy();
truncateStackTrace(throwableProxy);
return FilterReply.NEUTRAL;
}
private void truncateStackTrace(IThrowableProxy throwableProxy) {
if (throwableProxy == null) {
return;
}
StackTraceElementProxy[] originalStackTrace = throwableProxy.getStackTraceElementProxyArray();
if (originalStackTrace.length > MAX_STACK_TRACE_LENGTH) {
StackTraceElementProxy[] truncatedStackTrace = new StackTraceElementProxy[MAX_STACK_TRACE_LENGTH];
System.arraycopy(originalStackTrace, 0, truncatedStackTrace, 0, MAX_STACK_TRACE_LENGTH);
throwableProxy.setStackTraceElementProxyArray(truncatedStackTrace);
}
truncateStackTrace(throwableProxy.getCause());
}
}
然后,将这个过滤器添加到logback配置文件:
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="your.package.TruncateStackTraceFilter" />
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
回答:
可以考虑借助logstash-logback-encoder这个包来实现
参考https://blog.csdn.net/fu_huo_1993/article/details/124658831
以上是 logback对于过长的堆栈有什么处理方法? 的全部内容, 来源链接: utcz.com/p/945137.html