How to get a stack trace object in Ruby?
RubyStackStack TraceRuby Problem Overview
I need to get a stack trace object in Ruby; not to print it, just to get it to do some recording and dumping for later analysis. Is that possible? How?
Ruby Solutions
Solution 1 - Ruby
You can use Kernel.caller for this. The same method is used when generating stack traces for exceptions.
From the docs:
def a(skip)
caller(skip)
end
def b(skip)
a(skip)
end
def c(skip)
b(skip)
end
c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
c(2) #=> ["prog:8:in `c'", "prog:12"]
c(3) #=> ["prog:13"]
Solution 2 - Ruby
Try
Thread.current.backtrace.join("\n")
Solution 3 - Ruby
Try error.backtrace:
# Returns any backtrace associated with the exception.
# The backtrace is an array of strings, each containing either ``filename:lineNo: in `method’’’ or ``filename:lineNo.’‘
def a
raise "boom"
end
def b
a()
end
begin
b()
rescue => detail
print detail.backtrace.join("\n")
end
produces:
prog.rb:2:in `a'
prog.rb:6:in `b'
prog.rb:10
Solution 4 - Ruby
For Ruby 2.0+, you can use Kernel#caller_locations
. It is essentially the same as Kernel#caller
(covered in Sven Koschnicke's answer), except that instead of returning an array of strings, it returns an array of Thread::Backtrace::Location
objects. Thread::Backtrace::Location
provides methods such as path
, lineno
, and base_label
, which may be useful when you need access to specific details about the stack trace, and not just a raw string.
From the docs:
> ## caller_locations(start=1, length=nil) → array or nil
> ## caller_locations(range) → array or nil
>
> Returns the current execution stack—an array containing backtrace
> location objects.
>
> See Thread::Backtrace::Location
for more information.
>
> The optional start parameter determines the number of initial stack
> entries to omit from the top of the stack.
>
> A second optional length
parameter can be used to limit how many
> entries are returned from the stack.
>
> Returns nil
if start
is greater than the size of current execution
> stack.
>
> Optionally you can pass a range, which will return an array containing
> the entries within the specified range.
Usage example:
def a
caller_locations(0)
end
def b
a
end
def c
b
end
c.map(&:base_label)
#=> ["a", "b", "c", "<main>"]
Solution 5 - Ruby
Thread.current.backtrace
This will give you an array which contains all the lines that you may get in any normal backtrace.
Solution 6 - Ruby
You can create your own if you want as well. As demonstrated in Eloquent Ruby by Russ Olsen:
# define a proc to use that will handle your trace
proc_object = proc do |event, file, line, id, binding, klass|
puts "#{event} in #{file}/#{line} #{id} #{klass}"
end
# tell Ruby to use your proc on traceable events
set_trace_func(proc_object)