跨线程保留Java堆栈跟踪

vek*_*tor 9 java logging multithreading stack-trace

ExecutorService用来异步发送邮件,所以有一个类:

class Mailer implements Runnable { ...
Run Code Online (Sandbox Code Playgroud)

处理发送.对于(匿名)示例,将记录捕获的任何异常:

javax.mail.internet.AddressException: foo is bar
    at javax.mail.internet.InternetAddress.checkAddress(InternetAddress.java:1213) ~[mail.jar:1.4.5]
    at javax.mail.internet.InternetAddress.parse(InternetAddress.java:1091) ~[mail.jar:1.4.5]
    at javax.mail.internet.InternetAddress.parse(InternetAddress.java:633) ~[mail.jar:1.4.5]
    at javax.mail.internet.InternetAddress.parse(InternetAddress.java:610) ~[mail.jar:1.4.5]
    at mycompany.Mailer.sendMail(Mailer.java:107) [Mailer.class:?]
    at mycompany.Mailer.run(Mailer.java:88) [Mailer.class:?]
    ... suppressed 5 lines
    at java.lang.Thread.run(Thread.java:680) [?:1.6.0_35]
Run Code Online (Sandbox Code Playgroud)

不是很有帮助 - 我需要看到调用ExecutorService导致所有这一切的堆栈跟踪.我的解决方案是创建一个空Exception并将其传递给Mailer:

executorService.submit(new Mailer(foo, bar, new Exception()));
...
// constructor
public Mailer(foo, bar, Exception cause) { this.cause = cause; ...
Run Code Online (Sandbox Code Playgroud)

现在在异常的情况下,我想从另一个线程记录问题本身及其原因:

try {
  // send the mail...
} catch (Throwable t) {
  LOG.error("Stuff went wrong", t);
  LOG.error("This guy invoked us", cause);
}
Run Code Online (Sandbox Code Playgroud)

这很好但产生了两个日志.我想结合t,并cause进入一个异常和日志之一.在我看来,t造成的cause,所以使用cause.initCause(t)应该是正确的方式.并且工作.我看到了一个完整的堆栈跟踪:从呼叫始终一直到AddressException.

问题是,initCause()只能工作一次然后崩溃.问题1:我可以克隆Exception吗?我每次都克隆cause并初始化它t.

我试过了t.initCause(cause),但是马上就崩溃了.

问题2:是否有另一种智能方法来组合这两个例外?或者只是在另一个线程上下文中保留一个线程上下文用于记录目的?

Rea*_*tic 9

根据我的评论,这实际上是我的想法.请注意,我目前没有办法测试它.

你从父线程传递的是New Exception().getStackTrace().或者更好,正如@Radiodef评论的那样Thread.currentThread().getStackTrace().所以它基本上是一个StackTraceElement[]数组.

现在,您可以拥有以下内容:

public class CrossThreadException extends Exception {

    public CrossThreadException( Throwable cause, StackTraceElement[] originalStackTrace ) {

        // No message, given cause, no supression, stack trace writable
        super( null, cause, false, true );

        setStackTrace( originalStackTrace );
    }
}
Run Code Online (Sandbox Code Playgroud)

现在在您的catch子句中,您可以执行以下操作:

catch ( Throwable cause ) {
   LOG( "This happened", new CrossThreadException( cause, originalStackTrace ) );
}
Run Code Online (Sandbox Code Playgroud)

这将为您提供两个堆栈跟踪之间的边界.