ChatGPT解决这个技术问题 Extra ChatGPT

Get current stack trace in Ruby without raising an exception

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.

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.

V
Victor

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>'

Isn't it Kernel.caller - with a dot? Kernel.new.caller is not defined over here
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).
this one supports a parameter to skip any number of callers; see: stackoverflow.com/a/3829269/520567
For pretty print use - Rails.logger.debug caller.join("\n") or puts caller.join("\n"). Thanks.
S
Serjik

Try using

Thread.current.backtrace

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'",...]
A
Atul Khanduri

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