Skip to content

Reading Stack Traces #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/data/javadoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@
"Throwable.getCause()": "java.base/java/lang/Throwable.html#getCause()",
"Throwable.getStackTrace()": "java.base/java/lang/Throwable.html#getStackTrace()",
"Throwable.getSuppressed()": "java.base/java/lang/Throwable.html#getSuppressed()",
"Throwable.printStackTrace()": "java.base/java/lang/Throwable.html#printStackTrace()",

"Error": "java.base/java/lang/Error.html",
"Exception": "java.base/java/lang/Exception.html",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
---
id: lang.exceptions.stacktrace
title: Reading Stack Traces
slug: learn/exceptions/stacktrace
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this aims to be under exceptions portion, a slug_history element is needed.

type: tutorial-group
group: exceptions
layout: learn/tutorial-group.html
subheader_select: tutorials
main_css_id: learn
toc:
- What is a stack trace? {intro}
- A simple stack trace {simple}
- Printing stack traces {printing}
- The Caused by section {cause}
- Suppressed exceptions {suppressed}
- Reading bigger stack traces {bigger}
- Summary {summary}
description: "This article explains what stack traces are as well as how they can be read."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, the description is hard to grasp. Maybe consider rephrasing that "as well" to "and"?

last_update: 2024-04-27
author: ["DanielSchmid"]
---

<a id="intro">&nbsp;</a>
## What is a stack trace?

When an exception occurs in a Java program, a stack trace is often printed to the console. These stack traces often look scary, especially with complex frameworks but actually provide a lot of useful information for solving the issue causing the exeption. Specifically, stack traces show what exception occured as well as where exactly that happened and how the Java program got to that point.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, using an emotion (look scary) in this technical context needs a justification. If I were to just find out about stack traces...why are they scary? (maybe the amount of content they have makes them scary)....

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this section names induces to me the idea that I will find out what is a stacktrace and this definition is not clear enough "stack traces show what exception occured as well as where exactly that happened and how the Java program got to that point."
For me, a stack trace is a sequence of method calls that led to an exception being thrown in a Java program. A stack trace includes the names of the methods involved, along with the line numbers in the source code where the method calls occurred.


<a id="simple">&nbsp;</a>
## A simple stack trace

Before coming to complex stack traces, we first investigate a stack trace originating from a very simple Java application.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is the first section containing right after the introduction and since complex stack traces were discussed before, I would suggest to omit the "Before coming to complex stack traces" and keep the actual introductory comment.


```java
package com.example.someapplication;

public class StackTraceDemo {
public static void main(String[] args) {
someMethod();
}

private static void someMethod() {
throw new IllegalStateException("This is for demonstration.");
}
}
```

Running this class results in the following output.

```none
Exception in thread "main" java.lang.IllegalStateException: This is for demonstration.
at com.example.someapplication.StackTraceDemo.someMethod(StackTraceDemo.java:9)
at com.example.someapplication.StackTraceDemo.main(StackTraceDemo.java:5)
```

First of all, Java tells us that we got an exception in a thread called `main`. As we haven't used anything related to starting other threads, this is the only application thread in this example. In applications involving multiple threads, the thread name can be a useful piece of diagnostic information. This part may be omitted or look different depending on what printed the stack trace.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, this syntagm is confusing: "Java tells us that we got an exception in a thread called main". I assume you mean that the first line in the printed sequence contains the name of the thread where the exception was thrown.

Also...I avoid mentioning other threads involvement and just keep the explanation about main simply to The example code uses the main thread, provided by the JVM at the starting of the program's execution.


After the thread name, the full class name of the exception is shown. As we have thrown an `IllegalStateException` in our example, this is `java.lang.IllegalStateException`. If a message is associated with the exception, that message is included in the stack trace as well. In our case, we passed the message `This is for demonstration.` to the `IllegalStateException` constructor resulting in that text being part of the stack trace.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a message is associated with the exception, that message is included in the stack trace as well.

The above phrase implies an else case...so can there be a message in a stack trace not generated by an exception?


The lines following start with the word `at` and provide detailed information on where the exception occured and what method calls lead to the program getting to that. The first such line is `at com.example.someapplication.StackTraceDemo.someMethod(StackTraceDemo.java:9)`. This tells us that the exception occured in a method called `someMethod` in the class `StackTraceDemo` located in the package `com.example.someapplication`. It also informs us that the exception was thrown in line 9 of the file `StackTraceDemo.java`. With many IDEs, you can even click on the `StackTraceDemo.java:9` and the IDE automatically opens the relevant file and navigates to the line in question. If we take a look at that line, we can see that it is `throw new IllegalStateException("This is for demonstration");` which matches the stack trace. Similarly, the next line is `at com.example.someapplication.StackTraceDemo.main(StackTraceDemo.java:5)`. This line tells us that where `someMethod` was called. This happened in the `main` method of our `StackTraceDemo` class.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lines following start with the word at

Can you please tell me to what does start refer to?


Now, just from the stack trace, we can see where the exception was originating from. The `main` method called `someMethod` which threw an `IllegalStateException` with the message `This is for demonstration.`

<a id="printing">&nbsp;</a>
## Printing stack traces

Sometimes, it is necessary to print the stack trace to the console or some other location when catching an exception without re-throwing it. For doing that, we can make use of the [`printStackTrace`](javadoc:Throwable.printStackTrace()) method. Calling that method prints the stack trace of a given exception to `System.err`. We can adapt our previous program to do catch the exception and print the stack trace by ourselves.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes, it is necessary to print the stack trace

As a reader, I would expect an example on when should I print the stack trace.


```java
package com.example.someapplication;

public class StackTraceDemo {
public static void main(String[] args) {
try {
someMethod();
} catch(IllegalStateException e) {
System.out.println("An exception occured.");
e.printStackTrace();
}
System.out.println("The program is finished.");
}

private static void someMethod() {
throw new IllegalStateException("This is for demonstration.");
}
}
```

This will print the following to the console:

```none
An exception occured.
java.lang.IllegalStateException: This is for demonstration.
at com.example.someapplication.StackTraceDemo.someMethod(StackTraceDemo.java:15)
at com.example.someapplication.StackTraceDemo.main(StackTraceDemo.java:6)
The program is finished.
```

As the stack trace was printed to `System.err`, it will be displayed in red or highlighted in some way with most IDEs. If we want to print it to a different location, we can pass a [`PrintWriter`](javadoc:PrintWriter) or [`PrintStream`](javadoc:PrintStream) to the overloads of `printStackTrace`. For example, we could change `e.printStackTrace();` to `e.printStackTrace(System.out);` in order to print the stack trace to `System.out`.

<a id="cause">&nbsp;</a>
## The `Caused by` section

Sometimes, you might want to [throw an exception that was caused by a different exception](/learn/exceptions/throwing/#chained-exceptions).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to have a justification on why (as a developer) would I want to do that 😄 . Maybe mention some best practice.


```java
package com.example.someapplication;

public class StackTraceDemo {
public static void main(String[] args) {
someMethod();
}

private static void someMethod() {
try {
otherMethod();
} catch(Exception e) {
throw new RuntimeException(e);
}
}

private static void otherMethod() {
throw new IllegalStateException("This is for demonstration.");
}
}
```

When the stack trace of that exception is then printed, we can not only see information about the `RuntimeException` but also about the original `IllegalStateException`.

```none
Exception in thread "main" java.lang.RuntimeException: java.lang.IllegalStateException: This is for demonstration.
at com.example.someapplication.StackTraceDemo.someMethod(StackTraceDemo.java:12)
at com.example.someapplication.StackTraceDemo.main(StackTraceDemo.java:5)
Caused by: java.lang.IllegalStateException: This is for demonstration.
at com.example.someapplication.StackTraceDemo.otherMethod(StackTraceDemo.java:17)
at com.example.someapplication.StackTraceDemo.someMethod(StackTraceDemo.java:10)
... 1 more
```

Here, we can see that the `RuntimeException` was "Caused by" an `IllegalStateException` and we see the stack trace of both exceptions.

<a id="suppressed">&nbsp;</a>
## `Suppressed` exceptions

As with exceptions causing other exceptions, it is also possible that [an exception suppresses another](/learn/exceptions/catching-handling/#suppressed). Consider the following example.

```java
package com.example.someapplication;

import java.io.Closeable;
import java.io.IOException;

public class StackTraceDemo {
public static void main(String[] args) throws IOException {
try(UnclosableResource res = new UnclosableResource()) {
someMethod();
}
}

private static void someMethod() {
throw new IllegalStateException("This is for demonstration.");
}
}

class UnclosableResource implements Closeable {
@Override
public void close() throws IOException {
throw new IOException("This cannot be closed.");
}
}

```

Here, the `IOException` thrown in `UnclosableResource#close` is suppressed by the `IllegalStateException` thrown in `someMethod`. The stack trace printed by this application includes information about both exceptions.

```none
Exception in thread "main" java.lang.IllegalStateException: This is for demonstration.
at com.example.someapplication.StackTraceDemo.someMethod(StackTraceDemo.java:14)
at com.example.someapplication.StackTraceDemo.main(StackTraceDemo.java:9)
Suppressed: java.io.IOException: This cannot be closed.
at com.example.someapplication.UnclosableResource.close(StackTraceDemo.java:21)
at com.example.someapplication.StackTraceDemo.main(StackTraceDemo.java:8)
```

<a id="bigger">&nbsp;</a>
## Reading bigger stack traces

Especially when using complex libraries and frameworks, it is possible that stack traces become fairly long as there are often multiple `Caused By` sections. When that happens, it's important to know which part of the stack trace to focus on. If multiple exceptions are thrown and printed in short succession, it's often the first exception that is causing the other issues. As you want to know what originally caused the exception to happen, you typically want to first take a look at the last `Caused By` section of the first exception.

There may be many lines in the `Caused By` section related to framework code. As the problem is likely within your code, you should focus on the lines corresponding to classes/methods that are part of your code which you can see using the package name. However, you shouldn't forget that other parts of the stack trace may be important as well. If the last `Caused By` section doesn't contain anything about your application code, you might want to take a look at the `Caused By` sections before. Aside from that, the name of the exeption and the message often contain important information as well. Let's inspect an example of what that could look like.

```none
com.someframework.utils.ValidationException: java.lang.IllegalArgumentException: argument cannot be empty string!
at com.someframework.utils.SomeFrameworkClass.getOtherClass(SomeFrameworkClass.java:8)
at com.example.someapplication.StackTraceDemo.doSomething(StackTraceDemo.java:18)
at com.someframework.runner.ExecutingCode.callProgram(ExecutingCode.java:15)
at com.someframework.runner.ExecutingCode.lambda$0(ExecutingCode.java:7)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.IllegalArgumentException: argument cannot be empty string!
at com.someframework.utils.SomeFrameworkClass.validate(SomeFrameworkClass.java:15)
at com.someframework.utils.SomeFrameworkClass.getOtherClass(SomeFrameworkClass.java:6)
... 4 more
Exception in thread "some-framework-thread" com.someframework.runner.FrameworkException: java.lang.NullPointerException: Cannot invoke "com.someframework.utils.OtherFrameworkClass.doWork()" because "otherFrameworkClass" is null
at com.someframework.runner.ExecutingCode.lambda$0(ExecutingCode.java:9)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.NullPointerException: Cannot invoke "com.someframework.utils.OtherFrameworkClass.doWork()" because "otherFrameworkClass" is null
at com.someframework.utils.SomeFrameworkClass.doSomething(SomeFrameworkClass.java:20)
at com.example.someapplication.StackTraceDemo.doSomething(StackTraceDemo.java:22)
at com.someframework.runner.ExecutingCode.callProgram(ExecutingCode.java:15)
at com.someframework.runner.ExecutingCode.lambda$0(ExecutingCode.java:7)
... 1 more
```

First, we can see that two exceptions occured after each other. We first have a `ValidationException` which is then followed by a `FrameworkException`. We first inspect the last `Caused By` section of the first exception.

```none
Caused by: java.lang.IllegalArgumentException: argument cannot be empty string!
at com.someframework.utils.SomeFrameworkClass.validate(SomeFrameworkClass.java:15)
at com.someframework.utils.SomeFrameworkClass.getOtherClass(SomeFrameworkClass.java:6)
... 4 more
```

From its message, we can see that the exception occured because some argument should not be empty. However, all lines in that stack trace seem to come from the framework as we can see from the package name starting with `com.someframwork` which is (in this example) not the code we are working on. Due to this, we look at what was printed before the `Caused By` section.

```none
com.someframework.utils.ValidationException: java.lang.IllegalArgumentException: argument cannot be empty string!
at com.someframework.utils.SomeFrameworkClass.getOtherClass(SomeFrameworkClass.java:8)
at com.example.someapplication.StackTraceDemo.doSomething(StackTraceDemo.java:18)
at com.someframework.runner.ExecutingCode.callProgram(ExecutingCode.java:15)
at com.someframework.runner.ExecutingCode.lambda$0(ExecutingCode.java:7)
at java.base/java.lang.Thread.run(Thread.java:1583)
```

This part of the stack trace actually contains a line referring to our own code. Specifically, there is a line `at com.example.someapplication.StackTraceDemo.doSomething(StackTraceDemo.java:18)`. As we now know the exception is somehow originating from line 18 of `StackTraceDemo.java`, we take a look at the `StackTraceDemo` class and check what happens in line 18.

```java
package com.example.someapplication;

import com.someframework.utils.OtherFrameworkClass;
import com.someframework.utils.SomeFrameworkClass;
import com.someframework.utils.ValidationException;

public class StackTraceDemo {

private final SomeFrameworkClass frameworkObject;

public StackTraceDemo(SomeFrameworkClass frameworkObject) {
this.frameworkObject = frameworkObject;
}

public void doSomething() {
OtherFrameworkClass otherClass = null;
try {
otherClass = frameworkObject.getOtherClass("");
} catch (ValidationException e) {
e.printStackTrace();
}
frameworkObject.doSomething(otherClass);

}
}
```

Indeed, in this line we are passing an empty `String` to `frameworkObject.getOtherClass()` which is likely the reason the exception occured.

<a id="summary">&nbsp;</a>
## Summary

Even if stack traces may look intiminating at first, they are just a lot of diagnostic information helping you to find what caused the exception. As this information is often helpful in finding what caused the issue, you should typically not just ignore exceptions by adding a `System.err.println("An error occured");` or similar. Instead, either wrap the exception in another exception so that information about the original exception is preserved in a `Caused By` section or (if you actually want to continue executing after the exception occured) print the stack trace using the [`printStackTrace`](javadoc:Throwable.printStackTrace()) method.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Dan,

There is a small typo here: intimidating instead of intiminating


```java
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought: maybe you can consider refactoring this section to not end with a code section but with how looking at stack traces influence debugging.

try {
doSomething();
} catch(SomeException e) {
// System.err.println("Error!"); // This will lose all the useful information in the stack trace
throw new OtherException(e); // throw a different exception for someone else to handle preserving information about the original stack trace
// e.printStackTrace(); // if you don't want to rethrow it, consider printing the stack trace
}
```