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

Maybe I shall answer Robert's question at the same time :wink:

41. A logging mixin

That's a classical bug: off by one. :slight_smile:

I fail to understand Robert, look at this IRB session, as you see I
have prepared for your mail :wink:

irb(main):009:0> 41.succ
=> 43
irb(main):010:0> 43.pred
=> 41

irb(main):023:0> 42.class
=> TheAnswerToTheMeaningOfLiveTheUniverseAndEverything
irb(main):024:0> 43.class
=> Fixnum

Amazing! And I thought I knew something about numbers - at least below 100. :slight_smile:

And what do you say now about the "Mannschaft" :wink: They too were taking
"off by one" goal;)

Um, yes. What an awful game.

Cheers

robert

···

2008/6/26 Robert Dober <robert.dober@gmail.com>:

On Wed, Jun 25, 2008 at 6:55 PM, Robert Klemme > <shortcutter@googlemail.com> wrote:

2008/6/25 Robert Dober <robert.dober@gmail.com>:

--
use.inject do |as, often| as.you_can - without end

Iñaki Baz Castillo:

ara.t.howard:

ruby classes are evaluated top down at load time so, to
achieve the effect of init you just need to delete some code

and the 'init' code is called at load time.

The problem here is that those code is runned in load time, so if that
code depends on other project libraries or classes you must take care
of loading them first. This is not problem when calling "init" after
loading all the classes or when using "normal" objects (XXXX.new).

Right. I also figured out that in my case, I need to be able to reset
the config class to its default state when testing it, so I need an
init/reset method anyway.

-- Shot

···

--
I don't want to call a destructor on my objects, I want to call a destroyer.
Gozer has come for your memory, little PersistentNode! -- Joel Gluth, asr

ara.t.howard wrote:

this is a bad idea. try to use rails' code with other code that uses
ruby's logging class and you'll see what i mean.

Perhaps they modify ruby's logging class directly?

yup.

Rails.send('bad!')

I am proposing
something different: a selective injection of logging functionality into
a library's top-level module.

it's better just to keep things simple imho

I agree, but keep what simple? Implementation or interface?

Iñaki seems to want the interface to be simple, i.e. an automatic
logging facility that is (1) private to his library and (2) does not
require any explicit enabling (include Logging) or addressing (a LOG
constant).

your meta-programming bit will fail, for example, if someone does

module M
  extend AddLoggingToNested
end

That is easily solved by aliasing included() to extended():

module AddLoggingToNested
   # ... code from previous email ...

   class << self
     alias extended included
   end
end
--
Posted via http://www.ruby-forum.com/\.

yup i understand it all - because i've done it :wink: in the end i just decided it was easier to use a singleton/module level methods - not least of which because

   grep -R Logger .

ends up being used a ton in debugging the logs of a crashed program. anyhow, i'm not disagreeing - just noting that i've done both extensive and tend towards the simple and explicit pattern with logging - YMMV.

cheers.

a @ http://codeforpeople.com/

···

On Jun 23, 2008, at 2:59 PM, Suraj Kurapati wrote:
--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Correct. I haven't had a situation where I needed to do selective logging. I am open to suggestions for elegantly solving this problem.

cr

···

On Jun 26, 2008, at 3:53 AM, Andrea Fazzi wrote:

Chuck Remes ha scritto:

On a related note, I'm curious how people solve the problem of turning logging on and off during runtime.

I hate code like:

logger.log(:debug, "some msg") unless logger.nil?

I usually provide a null class for my loggers which silently swallow any messages sent to them if the logging function is disabled. It cleans up the code considerably.

Of course you can do that because you're setting a logger instance for each object with logging capability. But what to do if you are using a global logger instance and you wish to turn off logging selectively?

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/

···

On Aug 4, 2008, at 1:43 PM, Chuck Remes wrote:

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

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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

···

On Aug 4, 2008, at 3:35 PM, ara.t.howard wrote:

On Aug 4, 2008, at 1:43 PM, Chuck Remes wrote:

[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' }

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

···

On Aug 4, 2008, at 5:38 PM, Chuck Remes wrote:

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

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]

···

On Aug 4, 2008, at 6:30 PM, Chuck Remes wrote:

On Aug 4, 2008, at 5:38 PM, Chuck Remes wrote:

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

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 > 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.