Understanding StackTraces in Java
Tags: stacktraces, java
Published: 2020-05-21 08:17:09

<p>By guest author Thorben Janssen</p>



<p>The <em>StackTrace </em>is one of the key concepts in Java. It’s a call stack for the thread and lists all method calls since the start of the thread. You have probably seen its textual representation in your log file or console output. It gets printed to <em>System.out</em> whenever an exception is thrown and not handled by your application. The following snippet shows a typical example of such an output.<br /></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">java.lang.NumberFormatException: For input string: "123a45"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
	at java.base/java.lang.Long.parseLong(Long.java:699)
	at java.base/java.lang.Long.valueOf(Long.java:1151)
	at org.thoughts.on.java.TestStackTrace.testStackTrace(TestStackTrace.java:17)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	...</pre>



<p>Using monitoring tools, like FusionReactor, or by calling the <em>getStackTrace()</em> method on the current thread, you can access the <em>StackTrace </em>for all active threads in your JVM. But there are other ways to examine and work with a StackTrace.&nbsp;<br /></p>



<p>In most cases, you will not look at a <em>StackTrace </em>until you need to analyze an <em>Exception</em>. The <em>StackTrace </em>is part of an <em>Exception </em>object, and it shows all method calls that happened until the exception was thrown. That shows you where the exception occurred and how you reached that specific part of the code.&nbsp;<br /></p>



<p>In the next step, you can then analyze your code and find out what caused the exception. But that’s a topic for a different article. In this one, I want to tell you more about exceptions with their <em>StackTrace</em>s and all the information they provide, so that you have a better understanding of StackTraces in Java.</p>



<h2>Exceptions in Java</h2>



<p>An exception gets thrown whenever an error happens within a Java application. It gets represented by an object of the <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/StackTraceElement.html"><em>java.lang.Exception</em></a> class or one of its subclasses. The JDK provides you with a huge set of different <em>Exception </em>classes. If you want, you can also implement your own business exceptions.<br /></p>



<p>It’s a general <a href="https://howtodoinjava.com/best-practices/java-exception-handling-best-practices/">best practice</a> to use the most specific exception class for each error. A typical example for that is the <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Long.html#valueOf(java.lang.String)"><em>valueOf</em></a><em> </em>method of the <em>java.lang.Long</em> class. You can call it with a <em>java.lang.String</em> and it throws a <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/NumberFormatException.html"><em>java.lang.NumberFormatException</em></a> if the String has a format that can’t be parsed to a <em>Long</em>. The <em>NumberFormatException </em>is a subclass of the <em>IllegalArgumentException</em>, which indicates that an invalid argument value was passed to a method. As you can see, the <em>IllegalArgumentException </em>would describe the error situation, but the <em>NumberFormatException </em>is more specific and should be preferred.<br /></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private Long parseToLong(String s) {
	return Long.valueOf(s);
}

Long l;
try {
	l = parseToLong(s);
} catch (NullPointerException npe) {
	// handle NullPointerException
	log.error("No value provided. Using 0 as default.", npe);
	l = 0L;
}
log.info(l);</pre>



<p>Using the most specific exception class makes your code easier to read, and it enables you to implement a different catch clause for each exception class. This allows you to handle each error situation differently.<br /></p>



<p>You could, for example, decide to throw a <em>NullPointerException </em>if the provided String is null and throw a <em>NumberFormatException </em>if it doesn’t have the correct format.<br /></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private Long parseToLong(String s) {
	if (s == null) {
		throw new NullPointerException("String can't be null");
	}
	return Long.valueOf(s);
}</pre>



<p>In the code that calls this method, you can then implement 2 separate <a href="https://java-tutorial.org/exception-handling.html">catch blocks</a> that handle the <em>NullPointerException </em>and the <em>NumberFormatExceptions </em>in different ways. I did that in the following code snippet to provide different error messages for both situations. But you could, of course, use the same approach to implement a more complex error handling or to provide a fallback to default values.<br /></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Long l;
try {
	l = parseToLong(s);
} catch (NullPointerException npe) {
	// handle NullPointerException
	log.error("No value provided. Using 0 as default.", npe);
	l = 0L;
} catch (NumberFormatException nfe)	{
	// handle NullPointerException
	log.error("Provided value was invalid. Using 0 as default.", nfe);
	l = 0L;
}
log.info(l);</pre>



<h2>The structure of a StackTrace</h2>



<p>In the previous code snippet, I wrote log messages that contained the caught exception objects. The following snippet shows an example of such a message in the log file. Your application writes a similar message for all unhandled exceptions to your console.<br /></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">15:28:34,694  ERROR TestStackTrace:26 - Provided value was invalid. Using 0 as default.
java.lang.NumberFormatException: For input string: "123a45"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
	at java.base/java.lang.Long.parseLong(Long.java:699)
	at java.base/java.lang.Long.valueOf(Long.java:1151)
	at org.thoughts.on.java.TestStackTrace.parseToLong(TestStackTrace.java:39)
TestStackTrace.java:39
	at org.thoughts.on.java.TestStackTrace.testStackTrace(TestStackTrace.java:18)
TestStackTrace.java:18
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)</pre>



<p>As you can see, the log message contains a long list of class and method names. This is the textual representation of the <em>StackTrace </em>of the exception. Whenever a new method gets called, it gets added to the top of the stack, and after it got executed, it gets removed from the stack. Based on this approach, the last method that got called before the exception occurred is at the top of the <em>StackTrace </em>and logged first. The following elements in the <em>StackTrace </em>and lines in the log file show which methods were previously called to reach the part of the code that caused the exception.</p>



<h2>Using StackTraces to analyze incidents</h2>



<p>If you take another look at the previously shown <em>StackTrace</em>, you can see that the exception was created in the <em>forInputString </em>method of the <em>NumberFormatException </em>class. The actual problem occurred in the <em>parseLong </em>method of the <em>Long </em>class, which was called by the <em>valueOf </em>method of the same class, which was called by <em>parseToLong </em>method of my <em>TestStackTrace </em>class. As you can see, the StackTrace provided by the <em>NumberFormatException </em>clearly shows where the exception happened and which chain of method calls lead to it.<br /></p>



<p>This information is a good start to analyze an exception and to find the actual cause of it. But quite often, you will need more information to understand the issue. The <em>Exception </em>object and its <em>StackTrace </em>only describe which kind of error occurred and where it happened. But they don’t provide you with any additional information, like the values of certain variables. This can make it very hard to reproduce the error in a test case.</p>



<h2>How FusionReactor can help</h2>



<p>FusionReactor can provide you more information about the error situation. If you want, you can even debug the issues on your live system when it occurs next time.<br /></p>



<figure class="wp-block-image is-resized"><a href="http://www.fusion-reactor.com/wp-content/uploads/2020/01/Error-details.png" rel="prettyphoto"><img src="http://www.fusion-reactor.com/wp-content/uploads/2020/01/Error-details.png" alt="FusionRector Debugger error" class="wp-image-43457" width="602" height="327" srcset="http://www.fusion-reactor.com/wp-content/uploads/2020/01/Error-details.png 877w, http://www.fusion-reactor.com/wp-content/uploads/2020/01/Error-details-300x163.png 300w, http://www.fusion-reactor.com/wp-content/uploads/2020/01/Error-details-768x418.png 768w" sizes="(max-width: 602px) 100vw, 602px" /></a></figure>



<p>The only thing you need to do is to log into the web interface of the FusionReactor instance that monitors your application, select the exception from the “Error History” and go to the “Error Details” tab. There you can activate the debugger for this specific exception.<br /></p>



<p>After you’ve done that, FusionReactor will send you <a href="https://intergral.atlassian.net/wiki/spaces/FR82/pages/245553082/Production+Debugger+Email+Alert">an email</a> when the exception occurs again and pause the thread for a configured amount of time. The email contains the stack trace and information about the variable context. As long as the thread is paused, you can also use FusionReactor’s <a href="https://www.fusion-reactor.com/production-debugger-3/production-debugging-java/">Production Debugger</a> to debug the error in a similar way as you would in your IDE without affecting any of the other threads of your application.<br /></p>



<figure class="wp-block-image is-resized"><a href="http://www.fusion-reactor.com/wp-content/uploads/2020/01/FusionReactor-Debugger.png" rel="prettyphoto"><img src="http://www.fusion-reactor.com/wp-content/uploads/2020/01/FusionReactor-Debugger.png" alt="FusionReactor Debugger" class="wp-image-43463" width="602" height="327" srcset="http://www.fusion-reactor.com/wp-content/uploads/2020/01/FusionReactor-Debugger.png 858w, http://www.fusion-reactor.com/wp-content/uploads/2020/01/FusionReactor-Debugger-300x163.png 300w, http://www.fusion-reactor.com/wp-content/uploads/2020/01/FusionReactor-Debugger-768x417.png 768w" sizes="(max-width: 602px) 100vw, 602px" /></a></figure>