如何记录每个';在Ruby程序中调用了什么?
我继承了一大堆Ruby代码,坦率地说,对于像我这样的凡人来说,这几乎是不可能理解的。它实际上是Rspec单元测试代码,但它的结构“非常不寻常” 我希望能够运行代码,并将以下信息记录在某个地方:如何记录每个';在Ruby程序中调用了什么?,ruby,logging,methods,Ruby,Logging,Methods,我继承了一大堆Ruby代码,坦率地说,对于像我这样的凡人来说,这几乎是不可能理解的。它实际上是Rspec单元测试代码,但它的结构“非常不寻常” 我希望能够运行代码,并将以下信息记录在某个地方: 每个被调用的方法,包括定义该方法的类的名称,以及定义被调用方法的文件名(是的,我们在多个不同的文件中定义了相同的类/方法,很难知道调用的是哪一个) (可选)传递给调用的每个方法的参数 这样,我就可以开始尝试重构它了。没有它,由于代码库(20k+单元测试用例)的大小,将是一项非常困难的任务 我负担不起对
- 每个被调用的方法,包括定义该方法的类的名称,以及定义被调用方法的文件名(是的,我们在多个不同的文件中定义了相同的类/方法,很难知道调用的是哪一个)
- (可选)传递给调用的每个方法的参数
提前感谢这绝对是可能的——事实上,甚至有一种方法可以做到这一点!只需在您希望开始记录内容之前的代码中添加以下内容:
set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
您想要的秘方来自,如上所述:
- 设置跟踪函数(proc)=>proc
- set_trace_func(nil)=>nil
proc
建立为跟踪处理程序,如果参数为nil
,则禁用跟踪proc
最多使用六个参数:事件名称、文件名、行号、对象id、绑定和类名称<代码>过程在事件发生时调用。事件包括:c-call
(调用c-language例程)、c-return
(从c-language例程返回)、call
(调用Ruby方法)、class
(启动类或模块定义)、end
(完成类或模块定义)、行
(在新行上执行代码)、raise
(引发异常)和return
(从Ruby方法返回)。在进程上下文中禁用跟踪
下面是一个方便的示例:
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
您可以使用上面的格式字符串来获得想要记录的结果(例如,听起来您只对
call
事件感兴趣)。希望这会有所帮助,祝您好运,顺利完成所有这些单元测试!我想包括事件发生时的秒数以及每个函数花费的时间
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在
\uu bc\u crypt
上花了一分钟多的时间,最近,set\u trace\u func
被弃用了:
注意:此方法已过时,请改用跟踪点
我们可以使用TracePoint,它支持set\u trace\u func
:
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)
您是否考虑过静态分析器,或者您只是在寻找实际运行代码的东西?Doxygen输出了一些很好的调用方/被调用方图,还没有查看它是否支持Ruby,但调用图对于理解现有代码非常有用。我相当肯定Doxygen不支持Ruby-如果支持,它肯定会被用于ul给我,但我找不到任何关于Doxygen支持Ruby的信息。我更喜欢实际运行代码的东西,主要是因为require的处理顺序会影响多个中的哪一个(不相同)将使用给定方法的定义。正如我所说,这是一个丑陋的代码库…哇-这正是我所希望的。谢谢John!没问题。Ruby使用这种瑞士军刀的东西非常方便。听起来你的Bcrypt
成本相当高。T.J.Shuck(前15分钟左右).我相信我们当时确实解决了这个问题,而且这个问题肯定是B CRYPT。