如何记录Ruby程序中调用的每个方法?

时间:2010-02-08 03:50:20

标签: ruby logging methods

我继承了大量的Ruby代码,坦率地说,对于像我这样的凡人来说,几乎无法理解。它实际上是Rspec单元测试代码,但结构“非常不寻常”,以便很好地使用它。

我希望能够执行的是运行代码,并在某处记录以下信息:

  • 每个被调用的方法,包括定义方法的类的名称,以及已定义调用方法的文件名(是的,我们在多个不同的文件中定义了相同的类/方法,并且它是很难知道哪个被调用了)
  • (可选)传递给每个调用方法的参数

有了这个,我可以开始尝试重构它。如果没有它,由于代码库的大小(20k +单元测试用例),将它理顺起来将是一项非常困难的任务。

我无法进入并对正在运行的代码执行批量编辑,因为当您甚至在其周围使用苛刻的语言时(即经常)它会中断。相反,我需要能够在现有状态下对代码进行检测,或者对现有的代码进行最小的更改。

有没有一种方法可以在不对代码库进行批量更改的情况下记录此级别的详细信息?我已经看过Ruby分析器,看看它是否有用,它可能会有所帮助;我很好奇是否有更好的方法(特别是记录包含调用方法的文件名)。

提前致谢

3 个答案:

答案 0 :(得分:59)

这绝对是可能的 - 事实上,甚至还有一种方法!只需在您要开始记录事物的点之前在代码中的某处添加:

set_trace_func proc { |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}

你想要的秘诀来自 Kernel#set_trace_func ,如上所述:

  
      
  • set_trace_func(proc)=> PROC
  •   
  • set_trace_func(nil)=>零
  •   
     

proc建立为跟踪处理程序,如果参数为nil则禁用跟踪。 proc最多需要六个参数:事件名称,文件名,行号,对象ID,绑定和类的名称。只要发生事件,就会调用proc。事件是:c-call(调用C语言例程),c-return(从C语言例程返回),call(调用Ruby方法),class(启动类或模块定义),end(完成类或模块定义),line(在新行上执行代码),raise(引发异常)和{{ 1}}(从Ruby方法返回)。在proc的上下文中禁用跟踪。

这是一个方便的例子:

return

(注意:请不要在class Test def test a = 1 b = 2 end end set_trace_func proc { |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname } t = Test.new t.test 中尝试此操作,除非您想要一个巨大的文本滚动屏幕。)结果输出为:

irb

您可以使用上面的格式化字符串来获取您想要记录的结果(例如,听起来您只对 line test.rb:11 false c-call test.rb:11 new Class c-call test.rb:11 initialize Object c-return test.rb:11 initialize Object c-return test.rb:11 new Class line test.rb:12 false call test.rb:2 test Test line test.rb:3 test Test line test.rb:4 test Test return test.rb:4 test Test 事件感兴趣)。希望对所有这些单元测试进行分类有所帮助,祝你好运!

答案 1 :(得分:11)

最近,set_trace_func已被弃用:

  

注意:此方法已过时,请改用TracePoint。

我们可以使用支持set_trace_func的TracePoint代替:

trace = TracePoint.new(:call) do |tp|
  puts "#{tp.defined_class}##{tp.method_id} got called (#{tp.path}:#{tp.lineno})"
end

trace.enable
# do stuff here
trace.disable

这实际上比set_trace_func更强大,因为您可以在方便时启用和禁用。您可以选择性地挂钩以下事件::line, :class, :end, :call, :return, :c_call, :c_return, :raise, :b_call, :b_return, :thread_begin, :thread_end

这里有一个完整的例子:

class MyClass
  def initialize
  end
  def y
    z
  end
  def z
   1 + 1
  end
end

trace = TracePoint.new(:call) do |tp|
  puts "#{tp.defined_class}##{tp.method_id} got called (#{tp.path}:#{tp.lineno})"
end

trace.enable # note
MyClass.new.y
trace.disable
  # MyClass#initialize got called (./trace.rb:4)
  # MyClass#y got called (./trace.rb:7)
  # MyClass#z got called (./trace.rb:10)

答案 2 :(得分:4)

我希望包括事件发生的秒数,以及每个功能花费的时间

start = DateTime.now.strftime('%Q').to_i / 1000.0
set_trace_func proc { |event, file, line, id, binding, classname|
  now_ms = DateTime.now.strftime('%Q').to_i / 1000.0
  duration = '%.3f' % (now_ms - start)
  start = DateTime.now.strftime('%Q').to_i / 1000.0
  printf "%s %s %8s %s:%-2d %10s %8s\n", DateTime.now.strftime("%S.%L"), duration, event, file, line, id, classname
}

AdminUser.create(password: "password", password_confirmation: "password", email: email)

set_trace_func nil

我试图调试为什么创建用户并登录ActiveAdmin这么长时间。

05.761 0.000 c-return /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51       to_s   String
05.761 0.000   c-call /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51 __bc_crypt BCrypt::Engine
09.736 63.975 c-return /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51 __bc_crypt BCrypt::Engine
09.736 0.000   return /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:59 hash_secret BCrypt::Engine
09.736 0.000   c-call /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/password.rb:46        new    Class

从那以后我知道Ruby在__bc_crypt中花了一分多钟。

相关问题