| name | debug-with-tracepoint |
| description | Use when you need to know what a Ruby/Rails code path actually did at runtime - which methods ran, what arguments they got, what each one returned, and where an exception was first raised. Reach for this before adding puts/Rails.logger lines or reading call sites by hand, and whenever a value is wrong but you cannot tell which method produced it. |
Debugging with rails_tracepoint_stack
Traces one block of code and hands back the call tree: every method your app
defined that ran, with its arguments, its return value, and any exception,
nested by call depth. Gems, the framework and stdlib are filtered out.
When this beats the alternatives
| Question | Use this |
|---|
| "Which of these methods is returning nil?" | Yes - return values are the point |
| "Where did this exception actually start?" | Yes - the raise is tagged in the tree |
| "What is the real order of calls here?" | Yes - the tree shows nesting |
| "What is the value of one variable right here?" | No - a binding.irb is cheaper |
| "Is this endpoint slow?" | No - this measures nothing |
Run it
One shot, no config file, no server restart, output bounded:
bin/rails runner '
session = RailsTracepointStack.capture(max_depth: 4) do
Order.find(42).recalculate!
end
puts session.to_tree
'
Reading the output:
Order#recalculate! (app/models/order.rb:88) {}
Order#apply_discount (app/models/order.rb:102) {"total":200.0}
Discount#rate_for (app/models/discount.rb:12) {"order":"#<Order id: 42>"}
-> null <- rate_for returned nil, which is the bug
-> 200.0
-> 200.0
3 calls, 3 returns, 0 raises, 2 classes
- indentation is call depth
-> value is what the method on the line above returned
!! ArgumentError: message marks where an exception was raised
- a
-> null right after a !! is the frame unwinding, not a real return
render app/views/... is a template; the {...} after it are its locals
- the last line is the summary;
... truncated means a limit cut it short
An empty tree is an answer, not a failure
0 calls, 0 returns, 0 raises, 0 classes
no app code ran: 34025 traces from gems, the framework and Ruby itself were filtered out
This means the block ran entirely inside gems. A bare Order.where(...).map(&:name)
calls no method anyone in this app wrote — name is generated by ActiveRecord.
Do not conclude the tool is broken and fall back to puts. Either widen the
capture to include the code that calls into this, or accept that the bug is not
in app code. session.filtered_count is that number if you want it directly.
Keep the output small
capture is scoped to the calling thread and takes limits. Use them - an
unbounded capture of a real request is tens of thousands of lines.
| Option | Default | Use it to |
|---|
max_depth: | none | Stay near the top of the tree |
max_traces: | 5000 | Cap total lines |
max_string_length: | 200 | Keep big payloads readable |
max_collection_size: | 20 | Keep loaded associations readable |
capture_params: false | on | Show only the flow |
capture_return: false | on | Show only the calls |
threads: :all | current only | Include background threads |
To narrow by file instead, set patterns before capturing:
RailsTracepointStack.configure do |config|
config.file_path_to_filter_patterns << %r{app/services/}
config.ignore_patterns << %r{app/models/concerns/}
end
Other shapes of the same data
session.as_json
session.summary
session.result
session.error
capture re-raises whatever the block raised. To keep the traces in that
case, take the session from the block argument:
session = nil
begin
RailsTracepointStack.capture { |s| session = s; thing_that_blows_up }
rescue => error
puts session.to_tree
end
Worth knowing
- Requires the gem in the app:
gem "rails_tracepoint_stack".
- TracePoint slows the traced block down noticeably. Fine for a one-off
investigation, not for anything left running.
- Only methods defined in the app appear. If a method you expected is
missing, it is probably in a gem - add its path to
file_path_to_filter_patterns to force it in.
RAILS_TRACEPOINT_STACK_ENABLED=true traces the whole process to
log/rails_tracepoint_stack.log instead. Prefer capture unless you need
to trace something you cannot wrap in a block, such as boot.