Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I log every method that's called in a Ruby program?

I've inherited a large pile of Ruby code that's, frankly, close to impossible to understand for a mortal like myself. It's actually Rspec unit test code, but the structure is "highly unusual" to put it nicely.

What I'd like to be able to do is run the code, and have the following information logged somewhere:

  • every method that gets invoked, including the name of the class that defines the method, and the filename where the invoked method has been defined (yep, we've got the same class/method defined in multiple different files, and it's tough to know which is being invoked)
  • (optionally) the parameters passed to each method invoked

With that, I could start trying to refactor it. Without it, it's going to be a very difficult task to get it straightened out, due to the size of the code base (20k+ unit test cases).

I can't afford to go in and perform wholesale edits to the code being run, because it breaks when you even use harsh language around it (i.e. frequently). Instead, I need to be able to instrument the code in its existing state, or with minimal changes to what exists now.

Is there a way of logging this level of detail without making wholesale changes to the code base? I've had a look at the Ruby profiler to see if it could help, and it probably could; I'm curious if there's a better way (particularly logging the filename containing the invoked method).

Thanks in advance

like image 228
monch1962 Avatar asked Feb 08 '10 03:02

monch1962


People also ask

What does .call do in Ruby?

The purpose of the . call method is to invoke/execute a Proc/Method instance.

What does it mean when a method ends with in Ruby?

Ruby doesn't treat the ! as a special character at the end of a method name. By convention, methods ending in ! have some sort of side-effect or other issue that the method author is trying to draw attention to.

Does Method order matter in Ruby?

You could define method in any order, the order doesn't matter anything.

How does Rails logger work?

Rails is configured to create separate log files for each of the three default environments: development, test and production. By default it puts these log files in the log/ directory of your project. So if you open up that folder you'll see a development. log and test.


1 Answers

This is definitely possible -- in fact, there's even a method for it! Just add this somewhere in your code before the point that you want to start logging things:

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

The secret sauce you want comes from Kernel#set_trace_func, as noted above:

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

Establishes proc as the handler for tracing, or disables tracing if the parameter is nil. proc takes up to six parameters: an event name, a filename, a line number, an object id, a binding, and the name of a class. proc is invoked whenever an event occurs. Events are: c-call (call a C-language routine), c-return (return from a C-language routine), call (call a Ruby method), class (start a class or module definition), end (finish a class or module definition), line (execute code on a new line), raise (raise an exception), and return (return from a Ruby method). Tracing is disabled within the context of proc.

Here's a handy example:

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 

(Note: don't try this in irb unless you want a huge scrolling screen of text.) The resulting output is:

    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 

You can play around with the formatting string above to get just the results you want to log (for example, it sounds like you're only interested in call events). Hope that helps, and good luck with sorting through all those unit tests!

like image 183
John Feminella Avatar answered Sep 30 '22 20:09

John Feminella