views:

265

answers:

2

We have code to log data in our Ruby 1.8.6 web application. You call it roughly as follows:

$log.info("Some text here")

Now, in the logged output, I would like to include the module where that line appeared. I know that the Kernel#caller will give me an array where I can pull out the file and line number that the log line occurred, but I don't want that. I want the module, not the file name. The obvious solution is to modify the log line so that it reads like:

$log.info("Some text here", self.class.name)

and then parse the result. That's not going to work, though, because I am trying to extract this information in the default case. That is, I need the solution to work if the programmer forgot to specify the module, the second parameter to the log line.

Is there any way to do this? If not, I will just have to make do with the caller array; most of our modules are in separate directories, so this would be an 80% solution.

More complete example, please excuse minor syntax errors:

in file log.rb:

module Log
  class Logger
    def info(msg, mod = '')
      puts "Module: #{mod}  Msg: #{msg}"
    end
  end # class Logger
end # module Log
$log = Log::Logger.new

in file foo.rb:

module Foo
  class Bar
    def do_something
      # Do not pass in self.class.name.
      # We want the output to look like:
      # Module: Foo  Msg: I did something!
      $log.info "I did something!"
    end
  end # class Bar
end #module Foo
+2  A: 

Use call_stack.

First install it with RubyGems:

gem install call_stack

Then change log.rb to:

require 'rubygems'
require 'call_stack'

call_stack_on

module Log
  class Logger
    def info(msg, mod = '')
        mod = call_stack(2)[0][0] if mod == ''
        puts "Module: #{mod}  Msg: #{msg}"
    end
  end # class Logger
end # module Log
$log = Log::Logger.new

Works for me (Ruby 1.8.7).

$ ruby foo.rb
Module: Foo::Bar  Msg: I did something!
Firas Assaad
According to their site, we want to use ruby-debug instead, though the links appear broken. It also notes that this involves a substantial slow-down. Indeed, it may not even work. Still, the best solution so far, so I'm +1'ing this answer. Thanks.
ChrisInEdmonton
Here's ruby-debug: http://rubyforge.org/projects/ruby-debug/ , and yeah, it'd be slow. Ruby's default support for callstack inspection is pathetic and there's been numerous suggestions to improve it, so all you get is Kernel#caller. Any solution would either involve tracing or working with the C api, so I represented what seems to be the simplest one. I suggest that if you really need this functionality you enforce a coding convention and always have programmers specify the module when calling (no default value). Inconvenient, but fast. Or you could turn off call_stack when not debugging.
Firas Assaad
+1  A: 

Came across this post while looking for an answer for my own purposes.

Didn't find one that was appropriate, so I dug through the Ruby source and put together an extension. I've bundled it as a gem- should install without any problem so long as you are using Ruby 1.9.1:

sudo gem install sender

This will not work with Ruby 1.8, as 1.8 has a different model for tracking frames.

http://rubygems.org/gems/sender

Asher