When an error happens in your application, Ruby raises an exception and prints the stack trace to the log. In this article, we'll take a look at how to read the stack trace and how to use it to locate the source of an exception in your application.
The call stack
Whenever you call a method, Ruby places a stack frame on the call stack (or "runtime stack", but often just called "stack"). The stack frame is a memory allocation that holds the method's arguments, some space for internal variables, and the return address of the caller.
When one method (divide
) calls another method (Fixnum#/
, or /
for short), the latter is put on top of the stack, because it needs to execute before the first one can finish. When calling divide(8, 4)
in this example, Ruby will execute the following actions in order:
- Call
8./(4)
- Collect the division's result (
2
), and put it in a string - Collect the string (
"Dividing 8 by 4 gives 2"
), and print it to the console withputs
Stack traces
The stack trace (usually named "backtrace" in Ruby, but also referred to as "stack backtrace" and "stack traceback") is a human-readable representation of the stack at a specific moment while running your program. The stack trace for the call to the /
method would look like this, showing it was called in the divide
method on line 2, which was called in the <main>
method on line 5.
divide.rb:2:in `/' divide.rb:2:in `divide' divide.rb:5:in `<main>'
In the example above, calling our divide
method with a 0 as one of its arguments will result in a ZeroDivisionError
exception.
When that happens, Ruby will print the exception, along with the stack trace, to the console. The stack trace shows the stack in a human-readable format, with each method's location in your code, to help you pinpoint where the exception came from.
$ ruby divide_by_zero.rb divide_by_zero.rb:2:in `/': divided by 0 (ZeroDivisionError) from divide_by_zero.rb:2:in `divide' from divide_by_zero.rb:5:in `<main>'
-
On the first line in the stack trace above, we can see that a
ZeroDivisionError
was raised with "divided by 0" as its message. Besides the exception itself, we can see that it happened ondivide_by_zero.rb:2:in `/'
, which means the error was raised from the second line of our example file, from a method named/
(which isFixnum#/
, because the first argument is the Fixnum8
). -
The stack trace's second line shows where the
/
method was called from. In this case, it's from ourdivide
method on line 2. -
The last line shows that
divide
was called from<main>
, which refers to the initial context of a Ruby application. Generally, it means it was called from outside any "real" method.
NOTE: In Ruby 2.5, the logger prints the stack trace in reverse to fit them in a terminal window. The last line shows the exception, preceded by the line the exception happened on. The lines above that are the route up the stack.
Understanding stack traces
The stack trace gives you a dump of the current state of your call stack whenever an exception is raised, and is helpful in finding out where things went wrong.
Although the first line of the stacktrace shows the line the exception ocurred on, it doesn't always show the source of the error. In the example above, the program executed properly, but it couldn't handle the data passed to the divide
method. Walking up the stack trace leads to where it's called, which is the source of the problem.
As always, we’d love to know how you liked this article, if you have any questions about it, and what you’d like to read about next, so be sure to let us know at @AppSignal.