This was the tip of the week in the March 4, 2021 Ruby Weekly Newsletter.

Have you ever needed to know the execution stack for a call? Perhaps you’re looking to provide a useful logging message with the entire stacktrace? Or you want to know where a specific method was called? Or you’re writing a gem and need to know where a method is being used?

In any of these cases, you’re in luck because from Ruby 2.0 onwards we have access to Kernel#caller_locations which gives us an array of Thread::Backtrace::Location objects which represent the current execution stack.

Here’s a simple little example with three methods, where the third method calls the second method which calls the first method which in turn tells us the caller_locations:

def method_one

def method_two

def method_three

pp method_three

If we run this snippet (saved in a file called example.rb), we’ll get:

$ ruby example.rb
["example.rb:6:in `method_two'",
 "example.rb:10:in `method_three'",
 "example.rb:13:in `<main>'"]

We can see our stacktrace! Kernel#caller_locations can take two optional parameters which both work to limit the size of the array of Thread::Backtrace::Location objects we receive. The first (start) parameter tells how many stack entries to omit from the top of the stack. The second (length) parameter tells how many objects to include in the returned Array.

A common use of caller_locations is getting the first element to see the calling method. This can be done by passing in the arguments (1,1). If we modify our code slightly to use caller_locations(1,1), we’ll get:

$ ruby example.rb
["example.rb:6:in `method_two'"]

And we can see that method_two is calling method_one. Lastly, there are a few useful methods on Thread::Backtrace::Location objects. The most notable ones are #lineno, #label and #path which give us the line number, method name and file path respectively.