【问题标题】:Get stacktrace classes, not just files, in Ruby在 Ruby 中获取堆栈跟踪类,而不仅仅是文件
【发布时间】:2021-08-02 09:36:01
【问题描述】:

我的目标是查找当前堆栈中是否有 ActionJob::Base 以查看它是否来自后台作业。

我想办法是在 ruby​​ 中为我当前的位置调用类,这样我就可以在调用堆栈中搜索并找到 ActionJob::Base 祖先类。

在 ruby​​ 中有 a few waysthe current location 获取堆栈跟踪/回溯。他们涉及Thread.current.backtrace。这会导致字符串数组作为文件名而不是被调用的类。即

/Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:537:in `eval_input'
/Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:472:in `block in run'
/Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:471:in `catch'
/Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:471:in `run'
/Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:400:in `start'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/console/console_command.rb:70:in `start'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/console/console_command.rb:19:in `start'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/railties-6.1.3.2/lib/rails/commands/console/console_command.rb:102:in `perform'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/thor-1.1.0/lib/thor/command.rb:27:in `run'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/thor-1.1.0/lib/thor/invocation.rb:127:in `invoke_command'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/thor-1.1.0/lib/thor.rb:392:in `dispatch'
/Users/justin/dev/testarea/lamby_discovery/vendor/bundle-dev/ruby/2.7.0/gems/railties-6.1.3.2/lib/rails/command/base.rb:69:in `perform'

byebug 的backtrace 包含我正在寻找的信息(即IRB::Context),但似乎不可搜索,并且backtrace 是一个c 方法。

--> #0  IRB::Context.set_last_value(value#NilClass) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/context.rb:363
    #1  IRB::Context.evaluate(line#String, line_no#Integer, exception#NilClass) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/context.rb:459
    #2  block (2 levels) in IRB::Irb.block (2 levels) in eval_input at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:541
    #3  IRB::Irb.signal_status(status#Symbol) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:704
    #4  block in IRB::Irb.block in eval_input at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:538
    #5  block (2 levels) in RubyLex.block (2 levels) in each_top_level_statement at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/ruby-lex.rb:166
    ͱ-- #6  Kernel.loop at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/ruby-lex.rb:151
    #7  block in RubyLex.block in each_top_level_statement at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/ruby-lex.rb:151
    ͱ-- #8  Kernel.catch(*args) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/ruby-lex.rb:150
    #9  RubyLex.each_top_level_statement at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb/ruby-lex.rb:150
    #10 IRB::Irb.eval_input at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:537
    #11 block in IRB::Irb.block in run(conf#Hash) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:472
    ͱ-- #12 Kernel.catch(*args) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:471
    #13 IRB::Irb.run(conf#Hash) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:471
    #14 #<Class:IRB>.start(ap_path#NilClass) at /Users/justin/.rbenv/versions/2.7.3/lib/ruby/2.7.0/irb.rb:400

有什么建议吗?

【问题讨论】:

  • 另一种选择是在 ActionJob 的 before_perform 中设置一个 Thread.current 变量,然后您可以检查该变量是否存在以了解它是否在后台作业中
  • @maxpleaner 这是个好主意。考虑到任何涉及读取类堆栈的解决方案似乎都不现实,我可能最终会使用它。

标签: ruby


【解决方案1】:

我的想法是循环遍历caller(你可以限制,例如caller.first(10)),我将加载源代码然后检查源字符串include?是否是目标类(例如。 ActionJob::Base)。

这是我的解决方案,我使用gem solargraph 来帮助解析源代码(解析namspaceconstantsmethods,...),在您的情况下,我们需要找到任何调用者类superclassActionJob::Base,对吧? Solargraph 支持我们这样做:

gem 'solargraph', group: :development
bundle install

# debug helper
class Debug
  def self.search_super(caller, super_clazz)
    detect_paths = []
    caller.each do |caller_path|
      unless caller_path.nil?
        filepath = caller_path.split(':').first
        source = Solargraph::Source.load_string(File.read(filepath), filepath)
        map = Solargraph::SourceMap.map(source)
        detect = map.pins_by_class(Solargraph::Pin::Reference::Superclass)
        .select { |clazz| 
          clazz.to_s == super_clazz.to_s
        }
        unless detect.blank?
          detect_paths << filepath
        end
      end
    end
    detect_paths
  end
end

那么比如你想检测哪个作业调用了Demo#search这个方法,你可以在那个方法上设置一个断点

class Demo
  def search
    binding.pry
  end
end

并签入控制台

> Debug.search_super(caller, "ActionJob::Base")

注意我在我这边进行了测试以检测超类“ActionController::Base”(rails)并且它有效,但我还没有测试 ActionJobs。

【讨论】:

  • 哇,这令人印象深刻。抱歉,我应该澄清一下...我希望在生产中使用它,并且在 prod 中运行 Solargraph 不是我应该做的事情。但由于解决方案非常复杂,我假设考虑到标准库,这实际上是不可能的。
猜你喜欢
  • 2014-03-09
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多