I am using ExecutorService
to send mails asynchronously, so there is a class:
class Mailer implements Runnable { ...
That handles the sending. Any exception that gets caught is logged, for (anonymized) example:
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]
Not very helpful - I need to see the stacktrace that invoked the ExecutorService
that caused all of this. My solution is to create an empty Exception
and pass it into Mailer
:
executorService.submit(new Mailer(foo, bar, new Exception()));
...
// constructor
public Mailer(foo, bar, Exception cause) { this.cause = cause; ...
And now in the case of exception I want to log the problem itself and its cause from the other thread:
try {
// send the mail...
} catch (Throwable t) {
LOG.error("Stuff went wrong", t);
LOG.error("This guy invoked us", cause);
}
This works great but produces two logs. I want to combine t
and cause
into a single exception and log that one. In my opinion, t
caused cause
, so using cause.initCause(t)
should be the right way. And works. I see a full stack trace: from where the call originated all the way up to the AddressException
.
Problem is, initCause()
works only once and then crashes. Question 1: can I clone Exception
? I'd clone cause
and init it with t
every time.
I tried t.initCause(cause)
, but that crashes right away.
Question 2: is there another smart way to combine these 2 exceptions? Or just keep one thread context in the other thread context for logging purposes?
Example: Convert stack trace to a stringIn the catch block, we use StringWriter and PrintWriter to print any given output to a string. We then print the stack trace using printStackTrace() method of the exception and write it in the writer. Then, we simply convert it to string using toString() method.
Logging the stack traces of runtime exceptions assists developers in diagnosing runtime failures. However, unnecessary logging of exception stack traces can have many negative impacts such as polluting log files.
getStackTrace() method returns an array of stack trace elements, each representing one stack frame. The zeroth element of the array (assuming the array's length is non-zero) represents the top of the stack, which is the last method invocation in the sequence.
Just use new Throwable(). printStackTrace() method and it will print complete stack trace from where a method is called, into the console. Main difference between using dumpStack() and printStackTrace() is first entry in Stack, In case of dumpStack() first entry is always java.
Following my comment, this is actually what I had in mind. Mind you, I don't have a way to test it at the moment.
What you pass from your parent thread is New Exception().getStackTrace()
. Or better yet, as @Radiodef commented, Thread.currentThread().getStackTrace()
. So it's basically a StackTraceElement[]
array.
Now, you can have something like:
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 );
}
}
Now in your catch clause you can do something like:
catch ( Throwable cause ) {
LOG( "This happened", new CrossThreadException( cause, originalStackTrace ) );
}
Which will give you a boundary between the two stack traces.
You can use the Future<v>
object that returned from your submit invocation, then invoke the get()
method, if any exception occured during the task execution it will be re thrown.
Another option is to customize the default exception handler for the thread factory that creates the threads for your ExecutorService
. See for more details: Thread.UncaughtExceptionHandler
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With