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字节

所以,我的问题是:

  1. logback有没有什么参数设定,对这种超长的堆栈输出,进行优化输出的?
  2. 缘何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

回到顶部