Is it ellegant to use a global variable to store a Logger object?

  • Thread starter Iñaki Baz Castillo
  • Start date
A

ara.t.howard

I hate to resurrect this thread, but I have a problem that I can't
seem to solve and I didn't want to rehash the original suggestions.

I need a global Logger within a framework I wrote. The trouble is
that I need to be able to instantiate multiple independent copies of
this framework within the same Ruby runtime (specifically Jruby).

If I do this it doesn't work for more than one instance:

module Namespace
Logger = # instantiate some logger

module Submodule
class Klass
def foo
Logger.log("in Klass#foo")
end
end
end
end

I can load and instantiate this only once in the runtime. If I try
to do it a second time, it's like I have reopened the class/module
and Logger gets redefined. Each instantiation shares the same global
Logger which is *not* what I want. I want each one to have their own
instance.

Is this possible? Any recommendations for solving it?

cr

something similar to:



module Namespace
module Logger
def Logger.key *key
@key = key.first unless key.empty?
end

def instance
@instances = Hash.new{|h,k| @instances[key || :default ] ||= new}
end

%w( debug info warn error fatal ).each do |method|
module_eval <<-code
def Logger.#{ method }(*a, &b)
instance.#{ method }(*a, &b)
end
end
end
end
end


Namespace::Logger.key Thread.current.object_id # or something unique

Namespace::Logger.info{ 'foobar' }

a @ http://codeforpeople.com/
 
C

Chuck Remes

[snip]
I can load and instantiate this only once in the runtime. If I try
to do it a second time, it's like I have reopened the class/module
and Logger gets redefined. Each instantiation shares the same
global Logger which is *not* what I want. I want each one to have
their own instance.

Is this possible? Any recommendations for solving it?

cr

something similar to:



module Namespace
module Logger
def Logger.key *key
@key = key.first unless key.empty?
end

def instance
@instances = Hash.new{|h,k| @instances[key || :default ] ||= new}
end

%w( debug info warn error fatal ).each do |method|
module_eval <<-code
def Logger.#{ method }(*a, &b)
instance.#{ method }(*a, &b)
end
end
end
end
end


Namespace::Logger.key Thread.current.object_id # or something unique

Namespace::Logger.info{ 'foobar' }

For lurkers, here is some slightly corrected code (the above doesn't
compile).

require 'logger'

module Namespace
module Logger
def Logger.key= *key
@key = key.first unless key.empty?
end

def Logger.key
@key
end

def Logger.instance
@instances ||= Hash.new { |h,k| h[key || :default ]
= ::Logger.new(STDOUT) }
@instances[@key]
end

%w( debug info warn error fatal ).each do |method|
module_eval <<-code
def Logger.#{ method }(*a, &b)
instance.#{ method }(*a, &b)
end
code
end
end
end

Ara, thanks for the help.

cr
 
C

Chuck Remes

For lurkers, here is some slightly corrected code (the above doesn't
compile).

require 'logger'

module Namespace
module Logger
def Logger.key= *key
@key = key.first unless key.empty?
end

def Logger.key
@key
end

def Logger.instance
@instances ||= Hash.new { |h,k| h[key || :default ]
= ::Logger.new(STDOUT) }
@instances[@key]
end

%w( debug info warn error fatal ).each do |method|
module_eval <<-code
def Logger.#{ method }(*a, &b)
instance.#{ method }(*a, &b)
end
code
end
end
end

Hmmm... I puzzled through this all the way home and came to the
conclusion that I don't know why, or if, this works.

If we are saving information in an instance variable in a class
method, clearly that instance variable can be overwritten by another
writer, yes? These class methods are visible to anyone who can this
module in scope, therefore the @key variable can be pretty volatile.

This whole setup is predicated on WriterA calling
Namespace::Logger.key = <unique val> which is later used by
Namespace::Logger.instance as an index into a hash to lookup the
correct ::Logger object in @instances. However, there is no guarantee
that @key will remain <unique val> for WriterA. If WriterB comes along
and makes the same call, that writer replaces <unique val> with its
own value.

If that didn't happen, then how would @instances store references to
everyone's ::Logger objects? These variables must be shared for that
to work.

Right?

If true, I'm back to square one. Each writer would have to pass in
their own unique key so they lookup the right ::Logger object. Now
instead of injecting the ::Logger object into all of my classes, I
have to make sure the @key value is known by all.

Please correct me where I am wrong.

cr
 
C

Chuck Remes

For lurkers, here is some slightly corrected code (the above
doesn't compile).

require 'logger'

module Namespace
module Logger
def Logger.key= *key
@key = key.first unless key.empty?
end

def Logger.key
@key
end

def Logger.instance
@instances ||= Hash.new { |h,k| h[key || :default ]
= ::Logger.new(STDOUT) }
@instances[@key]
end

%w( debug info warn error fatal ).each do |method|
module_eval <<-code
def Logger.#{ method }(*a, &b)
instance.#{ method }(*a, &b)
end
code
end
end
end

Hmmm... I puzzled through this all the way home and came to the
conclusion that I don't know why, or if, this works.

If we are saving information in an instance variable in a class
method, clearly that instance variable can be overwritten by another
writer, yes? These class methods are visible to anyone who can this
module in scope, therefore the @key variable can be pretty volatile.

This whole setup is predicated on WriterA calling
Namespace::Logger.key = <unique val> which is later used by
Namespace::Logger.instance as an index into a hash to lookup the
correct ::Logger object in @instances. However, there is no
guarantee that @key will remain <unique val> for WriterA. If WriterB
comes along and makes the same call, that writer replaces <unique
val> with its own value.

If that didn't happen, then how would @instances store references to
everyone's ::Logger objects? These variables must be shared for that
to work.

Right?

If true, I'm back to square one. Each writer would have to pass in
their own unique key so they lookup the right ::Logger object. Now
instead of injecting the ::Logger object into all of my classes, I
have to make sure the @key value is known by all.

Please correct me where I am wrong.

Here's proof it doesn't work as currently written. I'm hoping there's
a trick I don't know about ruby that will allow this to work.

cr

require 'logger'

module Namespace
module Logger
def Logger.key= *key
@key = key.first unless key.empty?
end

def Logger.key
@key
end

def Logger.instance
@instances ||= Hash.new { |h,k| h[k || :default ]
= ::Logger.new(STDOUT) }
@instances[key]
end

%w( debug info warn error fatal ).each do |method|
module_eval <<-code
def Logger.#{ method }(*a, &b)
instance.#{ method }(*a, &b)
end
code
end
end

class Bar
def initialize(value)
Namespace::Logger.key = value
end

def foo
Namespace::Logger.info { "called with Logger.key
[#{Namespace::Logger.key}] " }
end
end
end

a = Namespace::Bar.new 17
b = Namespace::Bar.new 24

a.foo
b.foo


-- output --

I, [2008-08-04T19:06:50.597418 #91765] INFO -- : called with
Logger.key [24]
I, [2008-08-04T19:06:50.598877 #91765] INFO -- : called with
Logger.key [24]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,662
Latest member
sxarexu

Latest Threads

Top