166

I want to log the current backtrace (stacktrace) in a Rails 3 app without an exception occurring. Any idea how?

Why do I want this? I'm trying to trace the calls that are made when Rails looks for a template so that I can choose a part of the process to override (because I want to change the view path for a particular subclassed controller of mine).

I'd like to call it from the file: gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. I know that's not best practice, but I know it's downstream of the stack from where the search for templates occurs.

KL-7
  • 46,000
  • 9
  • 87
  • 74
JellicleCat
  • 28,480
  • 24
  • 109
  • 162
  • 4
    Dirty solution: raise an exception there, rescue it immediately and log `e.backtrace`. I've seen it in one of the projects I'm working with. Not the nicest approach, but it works. Hope to hear a better solution from someone else, though. – KL-7 Jun 20 '12 at 15:04

3 Answers3

207

You can use Kernel#caller:

# /tmp/caller.rb

def foo 
  puts caller # Kernel#caller returns an array of strings
end

def bar 
  foo 
end

def baz 
  bar 
end

baz

Output:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'
Victor
  • 1,680
  • 3
  • 22
  • 40
KL-7
  • 46,000
  • 9
  • 87
  • 74
  • Isn't it `Kernel.caller` - with a dot? `Kernel.new.caller` is not defined over here – ecoologic Feb 05 '13 at 06:26
  • 8
    No, technically `caller` is an instance method. Since `Kernel` module is included in every Ruby class (except `BasicObject` in 1.9), it's available as instance method on any object (it's private, though). You can't call it as `Kernel.new.caller` simply because you can't instantiate a module (it doesn't have `new` method). – KL-7 Feb 05 '13 at 07:01
  • this one supports a parameter to skip any number of callers; see: http://stackoverflow.com/a/3829269/520567 – akostadinov Dec 02 '14 at 11:59
  • 9
    For pretty print use - `Rails.logger.debug caller.join("\n")` or `puts caller.join("\n")`. Thanks. – Jignesh Gohel Mar 09 '15 at 18:57
40

Try using

Thread.current.backtrace
Serjik
  • 10,543
  • 8
  • 61
  • 70
Rajat Bansal
  • 885
  • 9
  • 7
  • 3
    The advantage of this answer is that it includes the **current method** in the backtrace, whereas `Kernel#caller` leaves out the current method. E.g. `MyClass.new.returns_caller => ["(irb):42:in 'irb_binding'",...]` isn't as helpful as `MyClass.new.returns_thread_backtrace => ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...]` – stwr667 Jul 12 '18 at 22:48
  • caller(0) includes current method – mikdiet Aug 08 '23 at 13:45
8

I use this to show a custom error page when exception are raised.

rescue_from Exception do |exception|
  logger.error exception.class
  logger.error exception.message
  logger.error exception.backtrace.join "\n"
  @exception = exception


  # ExceptionNotifier::Notifier.exception_notification env, @exception

  respond_to do |format|
    if [AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, ActionController::RoutingError, ActionController::UnknownAction].include?(exception.class)
      format.html { render :template => "errors/404", :status => 404 }
      format.js   { render :nothing => true, :status => 404 }
      format.xml  { render :nothing => true, :status => 404 }
    elsif exception.class == CanCan::AccessDenied
      format.html {
        render :template => "errors/401", :status => 401 #, :layout => 'application'
      }
      # format.js   { render :json => { :errors => [exception.message] }, :status => 401 }
      # format.js   { render :js => 'alert("Hello 401")' }
      format.js   { render :template => 'errors/401.js.erb' }

    else
      ExceptionNotifier::Notifier.exception_notification(env, exception).deliver        
      format.html { render :template => "errors/500", :status => 500 } #, :layout => 'im2/application' }
      # format.js   { render :nothing => true, :status => 500 }
      format.js   { render :template => 'errors/500.js.erb' }

    end
  end
end
Radix
  • 2,527
  • 1
  • 19
  • 43