6

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 
1
  • I don't see how you can do it, unless to pass self or binding to the logger object. You'd have to parse the file with the information from caller, which I'm sure you don't want to do. Commented Jan 7, 2010 at 18:33

3 Answers 3

3

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! 
Sign up to request clarification or add additional context in comments.

2 Comments

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.
Here's ruby-debug: 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.
3

A mixin solves the OP's specific requirements (meanwhile, +1 to Asher for solving the generic "who called me" case!).

module Log module Logger def info(msg) puts "Module: #{self} Msg: #{msg}" end end # class Logger end # module Log module Foo class Bar include Log::Logger def do_something # Do not pass in self.class.name. # We want the output to look like: # Module: Foo Msg: I did something! info "I did something!" end end # class Bar end # module Foo foobar = Foo::Bar.new foobar.do_something 

Comments

2

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.