ruby-on-railsrubydebuggingirbbyebug

Debug into method from production irb


When I look for a problem, for example with a specific ActiveRecord object, I often find myself doing the following on my production system:

# RAILS_ENV=production bundle exec

irb(main)> article = Article.find(123)
=> #<Article id: 123, title: "Foobar">
irb(main)> article.do_something(3)
NoMethodError: undefined method `id' for nil:NilClass

Sometimes I can't reproduce, why the line article.do_something(3) throws an error, so I want to debug it directly on my server, in production mode.

The problem now is: How do I step into the method #do_something with the argument 3 on the object / instance article?

Of course, one could set a breakpoint in that method, reload production and let all their customers wait on that breakpoint till I'm done debugging... But that wouldn't be the best idea.

So, is there a way to debug into a method of a specific instance from a running irb / pry session? (both would be ok)


Solution

  • After trying around and more googling, I think I found a solution that works for me.

    1. Log in to your rails console / irb / pry session
    2. Setup your case (e.g. load your models, require dependencies...), so that you can execute the code you want to debug in one line
    3. require 'byebug' (or require 'debugger' for older ruby versions)
    4. Now the interesting part: Put a debugger statement in front of the line you want to debug like this binding.pry; user.do_something or debugger; user.do_something
    5. Now you are in your debugger. Maybe you have to jump to the next line with next (or just n if you have shortcuts enabled) to step into your method.

    Here is a complete example from our production system:

    [1] pry(main)> require 'byebug'
    => true
    [2] pry(main)> user = User.find(2)
      User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
    => #<User id: 2, name="XXX XXX">
    [3] pry(main)> user.full_name
    NameError: undefined local variable or method address for #<User:0x0055b168663590>
    
    [4] pry(main)> binding.pry; user.full_name
    
    [68, 73] in /usr/src/app/app/models/user.rb
       68:   end
       69: 
       70:   def full_name
    => 71:     "#{address.firstname} #{address.last_name}"
       72:   end
       73: end
    (byebug)