【问题标题】:Get current stack trace in Ruby without raising an exception在 Ruby 中获取当前堆栈跟踪而不引发异常
【发布时间】:2012-06-22 17:56:36
【问题描述】:

我想在 Rails 3 应用程序中记录当前回溯(堆栈跟踪)不会发生异常。知道怎么做吗?

我为什么要这个?我正在尝试跟踪 Rails 查找模板时进行的调用,以便我可以选择要覆盖的过程的一部分(因为我想更改我的特定子类控制器的视图路径)。

我想从文件中调用它:gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb。我知道这不是最佳做法,但我知道它位于堆栈的下游,从那里开始搜索模板。

【问题讨论】:

  • 肮脏的解决方案:在那里引发异常,立即救援并记录e.backtrace。我在我正在处理的一个项目中看到了它。不是最好的方法,但它有效。不过,希望从其他人那里听到更好的解决方案。

标签: ruby stack-trace


【解决方案1】:

你可以使用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

输出:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'

【讨论】:

  • 不是Kernel.caller - 带点吗? Kernel.new.caller 没有在这里定义
  • 不,技术上caller 是一个实例方法。由于Kernel 模块包含在每个Ruby 类中(除了1.9 中的BasicObject),它可以作为任何对象的实例方法使用(尽管它是私有的)。不能仅仅因为不能实例化模块(它没有 new 方法)而将其称为 Kernel.new.caller
  • 这个支持一个参数跳过任意数量的调用者;见:stackoverflow.com/a/3829269/520567
  • 用于漂亮的打印 - Rails.logger.debug caller.join("\n")puts caller.join("\n")。谢谢。
【解决方案2】:

当引发异常时,我使用它来显示自定义错误页面。

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

【讨论】:

    【解决方案3】:

    尝试使用

    Thread.current.backtrace
    

    【讨论】:

    • 这个答案的优点是它在回溯中包含 当前方法,而 Kernel#caller 省略了当前方法。例如。 MyClass.new.returns_caller =&gt; ["(irb):42:in 'irb_binding'",...] 不如 MyClass.new.returns_thread_backtrace =&gt; ["(irb):38:in 'backtrace'","(irb):38:in 'returns_thread_backtrace'","(irb):43:in 'irb_binding'",...] 有用
    猜你喜欢
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2010-11-07
    相关资源
    最近更新 更多