异常栈 - 限制打印行数

在Java中,大量异常栈信息可能导致日志文件占用过多硬盘空间,甚至影响ELK等日志分析系统的效率。本文探讨如何限制异常栈的行数,通过分析`printStackTrace()`源码,提出自定义日志帮助类的1.0和2.0版本,实现对所有层次异常栈的行数限制,避免重复打印,并提供通过配置文件控制异常栈打印的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们经常通过 try catch 代码块包住一段可能出现异常的代码,同时在 catch 块中打印异常信息,如下所示:

public static void main(String args[]) {
    try {
        a();
    } catch (HighLevelException e) {
        e.printStackTrace();
    }
}

当然通常情况我们会使用日志框架,不会直接使用 e.printStackTrace() 方法打印异常栈,但是日志框架同样会输出整个异常栈的信息,如下所示:

logger.error("error message", e);

大部分情况下,我们是不需要查看整个异常栈的信息就可以定位问题的,而且大量的异常栈信息堆积到日志文件中,对硬盘的消耗较大。如果同步到 ELK 中,对日志的内容进行分析,对 ES 存储的消耗也会很大。

那怎么限制异常栈的行数呢?

首先,我们来分析 e.printStackTrace() 方法的源码,看看它是如何打印异常栈信息的。

printStackTrace() 方法在  java.lang.Throwable 类中,方法的调用链路如下所示:

java.lang.Throwable#printStackTrace() -> java.lang.Throwable#printStackTrace(java.io.PrintStream) -> java.lang.Throwable#printStackTrace(java.lang.Throwable.PrintStreamOrWriter)

我们来看真正执行打印的方法:

private void printStackTrace(PrintStreamOrWriter s) {
    // Guard against malicious overrides of Throwable.equals by
    // using a Set with identity equality semantics.
    Set<Throwable> dejaVu =
        Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
    dejaVu.add(this);

    synchronized (s.lock()) {
        // Print our stack trace
        s.println(this);
        StackTraceElement[] trace = getOurStackTrace();
        for (StackTraceElement traceElement : trace)
            s.println("\tat " + traceElement);

        // Print suppressed exceptions, if any
        for (Throwable se : getSuppressed())
            se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);

        // Print cause, if any
        Throwable ourCause = getCause();
        if (ourCause != null)
            ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
    }
}

public StackTraceElement[] getStackTrace() {
    return getOurStackTrace().clone();
}

private synchronized StackTraceElement[] getOurStackTrace() {
    // Initialize stack trace field with information from
    // backtrace if this is the first call to this method
    if (stackTrace == UNASSIGNED_STACK ||
        (stackTrace == null && backtrace != null) /* Out of protocol state */) {
        int depth = getStackTraceDepth();
        stackTrace = new StackTraceElement[depth];
        for (int i=0; i < depth; i++)
            stackTrace[i] = getStackTraceElement(i);
    } else if (stackTrace == null) {
        return UNASSIGNED_STACK;
    }
    return stackTrace;
}

通过上面的代码,可以知道 e.printStackTrace() 方法中打印的内容是 StackTraceElement[],而且在接下来会输出 suppressed exceptions 和 cause 信息。

getStackTrace() 已经提供了 public 方法,可以通过 e.getStackTrace() 方式直接获取,不需要通过反射调用下面的私有方法。

新建自定义日志帮助类,限制异常栈的打印行数。

在自定义类之前,先准备两个会输出异常信息的测试类,一个(ExceptionTest1)会输出 Caused by,一个(ExceptionTest2)会输出 Suppressed。

public class ExceptionTest1 {
    public static void main(String args[]) {
        try {
            a();
        } catch (HighLevelException e) {
            System.out.println(LoggerHelper.printTop10StackTrace(e));
        }
    }

    static void a() throws HighLevelException {
        try {
            b();
        } catch (MidLevelException e) {
            throw new HighLevelException(e);
        }
    }

    static void b() throws MidLeve
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值