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

They are shared by all subclasses, look at this
irb(main):006:0> class A
irb(main):007:1> @@a = self
irb(main):008:1> end
=> A
irb(main):009:0> class B < A
irb(main):010:1> @@a = self
irb(main):011:1> end
=> B
irb(main):013:0> A.send :class_variable_get, :@@a
=> B

this was bad enough for me never wanting have to do with them again,
now look at this

irb(main):014:0> class A
irb(main):015:1> @a = self
irb(main):016:1> end
=> A
irb(main):017:0> class B < A
irb(main):018:1> @a = self
irb(main):019:1> end
=> B
irb(main):020:0> A.instance_variable_get :@a
=> A
irb(main):021:0> B.instance_variable_get :@a
=> B

HTH
Robert

···

On Fri, Jun 27, 2008 at 11:40 AM, Robert Dober <robert.dober@gmail.com> wrote:

On Fri, Jun 27, 2008 at 10:17 AM, Shot (Piotr Szotkowski) <shot@hot.pl> wrote:

Iñaki Baz Castillo:

El Jueves, 26 de Junio de 2008, Shot (Piotr Szotkowski) escribió:

module MyProgram
  class Logger
  class << self
    def debug …
      …
    end
  end
  end
end

Ok, this seems really ellegant and I've tested that I can call
"Logger.debug..." in any submodule/subclass into the program :slight_smile:

:slight_smile:

Just a question: Logger must use an instance of a class (real
class Logger), so to store it I think the best option is using
a @@class_variable into Logger module

No – by using class << self, you're operating on the Logger object
(an instance of the class Class), and you can access its *instance*
variables.

<snip>
Amen

grep --recursive '@@' lib && echo "Classvariables are the root of all evil ;)"

Cheers
Robert

--
http://ruby-smalltalk.blogspot.com/

---
AALST (n.) One who changes his name to be further to the front
D.Adams; The Meaning of LIFF

Iñaki Baz Castillo:

module ArtDecomp class Config
class << self
attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
def init
@debug = false
@processes = 1
@qu_method = :graph_merger
@qv_method = :graph_merger
@silicone = Set[Arch[4,2], Arch[5,1]]
end

end
end end
ArtDecomp::Config.init

The only I don't like about the above code is the need of using .init
method explicitely. It could be nice if "initialize" would also work
automatically in some way, is not possible? (of course I understand
that we are not creating an instance here).

Yeah, that would be the cherry on top. :slight_smile:

I couldn’t come up with any nice solution; note that you can initialize
this class anywhere, so if you have some other place that does any kind
of program setup, you can do it there. Otherwise, doing it right after
the class is defined is the nicest solution in my opinion.

-- Shot

···

El Viernes, 27 de Junio de 2008, Shot (Piotr Szotkowski) escribió:

--
Our chief weapons are who, ps -aux, kill -9, and
a fanatical devotion to fastboot. -- Sun User Group

Suraj Kurapati wrote:

  # recursively adds Logging functionality to all nested classes
  extender = lambda do |k|
    k.constants.map {|c| k.const_get c }.
    select {|c| c.is_a? Class or c.is_a? Module }.

Hmm, I should have used duck-typing in the above line (all the Java
programming I've done recently has corrupted me ;-). So the above line
should be written as:

  select {|c| c.respond_to? :constants and c.respond_to? :const_get }.

···

    each do |c|
      c.extend Logging
      extender[c]
    end
  end

--
Posted via http://www.ruby-forum.com/\.

Now things are becoming quite complex.
Logging is a global task and thusly some form of global handling is perfectly ok IMHO. Using a global variable has the advantage that you do not collide with instance methods (which you do if you place #debug etc. in all classes either via inheritance, meta programming or other means). IMHO the only bad aspect about the global is that you cannot ensure automatic initialization. An alternative would be to add a single method to Kernel that lazily initializes a Logger instance.

My 0.02 EUR.

Kind regards

  robert

···

On 23.06.2008 03:29, Suraj Kurapati wrote:

Iñaki Baz Castillo wrote:

Humm, I don't like adding "include Logging" to every classes I use since
sometimes I use objects, sometimes classes, so I'd also need to "extend" some classes not just "include".

You could use meta programming to include Logging in all nested classes and modules of your top-level module:

I am by no means qualified to judge DIin general and I have to admit
that my wording might have been indeed to offensive. Scusa per questo.
It is however intriguing to me when dependency injection is a good
solution. I am sure that there is plenty space for its application,
maybe even for logging in a context more specific than the one given
by OP. I believe that the main concern would be the loose coupling of
objects, IIUC you would need a Container method for each class *and*
each class would need to have an adapted initialize method, well that
just really seems too much work to me.

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

41. A logging mixin

module MyLogger
   logger = Logger::new $stderr
   define_method :log do logger end
end

now all you have to do is to do include MyLogger in all your classes
and it is sufficient to change the MyLogger module once only, that of
course has not yet any advantage over using a global logger variable
or constant.
But you might have much more flexibility by generalizing the approach
above as follows

module MyLogger
   logger1 = ...
   logger2 = ...
   class << self; self end.module_eval do
        define_meyhod :included do |into_module|
            logger =
            case into_module
            when Type1Module
                logger1
            when Type2Module
               logger2
            else
               Logger::new $stderr
            end
            into_module.module_eval do
                 define_method :log do logger end
            end*

That would give you pretty easy central control of logger capabilities
on one single point of your project in a more transparent way.
Actually DI makes him carry quite a have load, does it not?

Cheers
Robert

···

On Wed, Jun 25, 2008 at 3:36 PM, Andrea Fazzi <andrea.fazzi@alca.le.it> wrote:

Robert Dober ha scritto:

On Wed, Jun 25, 2008 at 11:22 AM, Robert Klemme >> <shortcutter@googlemail.com> wrote:

2008/6/24 Iñaki Baz Castillo <ibc@aliax.net>:

El Martes, 24 de Junio de 2008, Andrea Fazzi escribió:

require 'logger'

class Container
def logger
   @logger ||= Logger.new STDOUT
end
def foo
   Foo.new(logger)
end
end

class Foo
def initialize(logger)
   @logger = logger
end
def bar
   @logger.info('Foo#bar invoked.')
end
end

c = Container.new
c.foo.bar

That's a cool solution :slight_smile:

I do not think so. Reasons: it clutters every instance with a
reference which can have a significant impact on memory if there are a
lot objects. Then, you have to change a class's #initialize signature
for all classes that do want to do logging and also manually code the
assignment inside the class. Accessing a logger from a global context
(whichever way you do it) saves memory and is less tedious.

Kind regards

robert

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

Could not agree more with you. May I add some other reasons.
This approach vioaltes principles we regard rather highly on this list
It is not DRY, you are really repeating yourself and in case of the
metaprogramming solutions you let Ruby repeat itself.
It is just much less simple than necessary.
You spread code dependencies all over the place, as a matter of fact
the expression "dependency injection" says it allready it is almost as
putting a virus (with constructive behavior) into your code, but can
you imagine how much more work refactoring will become?

My order of preference would be
1. $logger or $LOGGER
2. Logger

43. Kernel::log (or Object.log)

Cheers

Hi Robert,

I would known if, in your opinion, DI is not DRY in general or you are
referring to the particular case of logging. Moreover, which are alternative
DRY solutions to DI that guarantees loose coupling between objects?

Andrea

--
http://ruby-smalltalk.blogspot.com/

---
Les mêmes questions qu'on se pose
On part vers où et vers qui
Et comme indice pas grand-chose
Des roses et des orties.
-
Francis Cabrel

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

   module ArtDecomp::Config
     @debug = false
     @processes = 1
     @qu_method = :graph_merger
     @qv_method = :graph_merger
     @silicone = Set[Arch[4,2], Arch[5,1]]

     class << self
       attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
     end
   end

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

a @ http://codeforpeople.com/

···

On Jun 28, 2008, at 9:50 AM, Shot (Piotr Szotkowski) wrote:

El Viernes, 27 de Junio de 2008, Shot (Piotr Szotkowski) escribió:

module ArtDecomp class Config
class << self
  attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
  def init
    @debug = false
    @processes = 1
    @qu_method = :graph_merger
    @qv_method = :graph_merger
    @silicone = Set[Arch[4,2], Arch[5,1]]
  end

end
end end
ArtDecomp::Config.init

The only I don't like about the above code is the need of using .init
method explicitely. It could be nice if "initialize" would also work
automatically in some way, is not possible? (of course I understand
that we are not creating an instance here).

Yeah, that would be the cherry on top. :slight_smile:

I couldn’t come up with any nice solution; note that you can initialize
this class anywhere, so if you have some other place that does any kind
of program setup, you can do it there. Otherwise, doing it right after
the class is defined is the nicest solution in my opinion.

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

Ok, thanks all for your help and advices :slight_smile:

···

2008/6/23, Robert Klemme <shortcutter@googlemail.com>:

Now things are becoming quite complex.
Logging is a global task and thusly some form of global handling is
perfectly ok IMHO. Using a global variable has the advantage that you do
not collide with instance methods (which you do if you place #debug etc. in
all classes either via inheritance, meta programming or other means). IMHO
the only bad aspect about the global is that you cannot ensure automatic
initialization. An alternative would be to add a single method to Kernel
that lazily initializes a Logger instance.

--
Iñaki Baz Castillo
<ibc@aliax.net>

Robert Klemme wrote:

I�aki Baz Castillo wrote:

Humm, I don't like adding "include Logging" to every classes I use since
sometimes I use objects, sometimes classes, so I'd also need to "extend"
some classes not just "include".

You could use meta programming to include Logging in all nested classes
and modules of your top-level module:

Now things are becoming quite complex.

Not really. You can refactor that bit of meta programming into its own
module:

  module AddLoggingToNested
    def self.included target
      # ... code from previous e-mail ...

      extender[target]
    end
  end

Now, you can add this propagation of logging functionality to any
top-level module or class by simply doing:

  module MyProjectTopLevel
    # ...

    include AddLoggingToNested
  end

Note that the include musts be done *after* all nested classes have been
defined. That is why I put that include() statement at the bottom of
the module's code.

···

On 23.06.2008 03:29, Suraj Kurapati wrote:

--
Posted via http://www.ruby-forum.com/\.

Robert Dober ha scritto:

  

Robert Dober ha scritto:
    

require 'logger'

class Container
def logger
   @logger ||= Logger.new STDOUT
end
def foo
   Foo.new(logger)
end
end

class Foo
def initialize(logger)
   @logger = logger
end
def bar
   @logger.info('Foo#bar invoked.')
end
end

c = Container.new
c.foo.bar

That's a cool solution :slight_smile:

I do not think so. Reasons: it clutters every instance with a
reference which can have a significant impact on memory if there are a
lot objects. Then, you have to change a class's #initialize signature
for all classes that do want to do logging and also manually code the
assignment inside the class. Accessing a logger from a global context
(whichever way you do it) saves memory and is less tedious.

Kind regards

robert

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

Could not agree more with you. May I add some other reasons.
This approach vioaltes principles we regard rather highly on this list
It is not DRY, you are really repeating yourself and in case of the
metaprogramming solutions you let Ruby repeat itself.
It is just much less simple than necessary.
You spread code dependencies all over the place, as a matter of fact
the expression "dependency injection" says it allready it is almost as
putting a virus (with constructive behavior) into your code, but can
you imagine how much more work refactoring will become?

My order of preference would be
1. $logger or $LOGGER
2. Logger

43. Kernel::log (or Object.log)

Cheers

Hi Robert,

I would known if, in your opinion, DI is not DRY in general or you are
referring to the particular case of logging. Moreover, which are alternative
DRY solutions to DI that guarantees loose coupling between objects?

Andrea

I am by no means qualified to judge DIin general and I have to admit
that my wording might have been indeed to offensive. Scusa per questo.
  
Don't worry :slight_smile:

It is however intriguing to me when dependency injection is a good
solution. I am sure that there is plenty space for its application,
maybe even for logging in a context more specific than the one given
by OP. I believe that the main concern would be the loose coupling of
objects, IIUC you would need a Container method for each class *and*
  
Hum .. I don't understand here. What do you intend for 'a Container method for each class'?
In my implementation there is only a Container class that is the central configuration point of the system.

each class would need to have an adapted initialize method, well that
  
Yes, and indeed this is very annoying. When the system grows up you have to do with long constructor signatures:

Foo.new(logger, service, another_service ... )

But I wonder if there is a better method to keep things decoupled..

···

On Wed, Jun 25, 2008 at 3:36 PM, Andrea Fazzi <andrea.fazzi@alca.le.it> wrote:

On Wed, Jun 25, 2008 at 11:22 AM, Robert Klemme >>> <shortcutter@googlemail.com> wrote:

2008/6/24 Iñaki Baz Castillo <ibc@aliax.net>:

El Martes, 24 de Junio de 2008, Andrea Fazzi escribió:

just really seems too much work to me.

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

41. A logging mixin

module MyLogger
   logger = Logger::new $stderr
   define_method :log do logger end
end

now all you have to do is to do include MyLogger in all your classes
and it is sufficient to change the MyLogger module once only, that of
course has not yet any advantage over using a global logger variable
or constant.
But you might have much more flexibility by generalizing the approach
above as follows

module MyLogger
   logger1 = ...
   logger2 = ...
   class << self; self end.module_eval do
        define_meyhod :included do |into_module|
            logger =
            case into_module
            when Type1Module
                logger1
            when Type2Module
               logger2
            else
               Logger::new $stderr
            end
            into_module.module_eval do
                 define_method :log do logger end
            end*

That would give you pretty easy central control of logger capabilities
on one single point of your project in a more transparent way.
Actually DI makes him carry quite a have load, does it not?

Cheers
Robert

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

robert

···

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

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

41. A logging mixin

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

Yeah! I just realized of that today doing some tests. It's really great!

···

El Sábado, 28 de Junio de 2008, ara.t.howard escribió:

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

   module ArtDecomp::Config
     @debug = false
     @processes = 1
     @qu_method = :graph_merger
     @qv_method = :graph_merger
     @silicone = Set[Arch[4,2], Arch[5,1]]

     class << self

attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
     end
   end

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

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

Ah, so perfect! Thanks a lot!

-- Shot

···

--
Boot XP under Parallels on the Mac and it says, "Your
computer may be at risk." No shit. -- Michael Feathers

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. it's better just to keep things simple imho - your meta-programming bit will fail, for example, if someone does

module M
   extend AddLoggingToNested
end

i agree with robert that logging is (typically) a global task and thus the interface should reflect that. i'd use something like

module Namespace

   module Logger
     Instance = ::Logger.new STDERR

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

end

which does three things

  - box your methods in a namespace

  - allow access via: Logger.info ....

  - allow access via: include Logger, info ...

perhaps it might make sense to auto-mixin, but tha'ts a layer on top of this.

kind regards.

a @ http://codeforpeople.com/

···

On Jun 23, 2008, at 9:10 AM, Suraj Kurapati wrote:

Not really. You can refactor that bit of meta programming into its own
module:

module AddLoggingToNested
   def self.included target
     # ... code from previous e-mail ...

     extender[target]
   end
end

Now, you can add this propagation of logging functionality to any
top-level module or class by simply doing:

module MyProjectTopLevel
   # ...

   include AddLoggingToNested
end

Note that the include musts be done *after* all nested classes have been
defined. That is why I put that include() statement at the bottom of
the module's code.

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

I typically provide a setter on my classes for configuring the log object. Hardly perfect, but it keeps the constructor signatures short. I am liking this suggestion to use a global variable though. I think I'll experiment.

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.

Is there another way to solve the problem of turning logging on/off at runtime without lots of conditionals?

cr

···

On Jun 25, 2008, at 11:04 AM, Andrea Fazzi wrote:

Robert Dober ha scritto:

each class would need to have an adapted initialize method, well that

Yes, and indeed this is very annoying. When the system grows up you have to do with long constructor signatures:

Foo.new(logger, service, another_service ... )

But I wonder if there is a better method to keep things decoupled..

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

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

Robert

···

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

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

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:

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

···

El Domingo, 29 de Junio de 2008, Shot (Piotr Szotkowski) escribió:

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.

Ah, so perfect! Thanks a lot!

--
Iñaki Baz Castillo

Could you please explain me what the last line does? (::Logger.new)

Thanks a lot.

···

El Lunes, 23 de Junio de 2008, ara.t.howard escribió:

i agree with robert that logging is (typically) a global task and thus
the interface should reflect that. i'd use something like

module Namespace

   module Logger
     Instance = ::Logger.new STDERR

--
Iñaki Baz Castillo

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? 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/\.

<snip>

I hate code like:

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

That is why my debugging or logging routines normally look like this

def logger *args
   return unless $LOGGING
   ....
end

HTH
Robert

···

On Wed, Jun 25, 2008 at 9:00 PM, Chuck Remes <cremes.devlist@mac.com> wrote:
--
http://ruby-smalltalk.blogspot.com/

---
Les mêmes questions qu'on se pose
On part vers où et vers qui
Et comme indice pas grand-chose
Des roses et des orties.
-
Francis Cabrel

Chuck Remes ha scritto:

Robert Dober ha scritto:

each class would need to have an adapted initialize method, well that

Yes, and indeed this is very annoying. When the system grows up you have to do with long constructor signatures:

Foo.new(logger, service, another_service ... )

But I wonder if there is a better method to keep things decoupled..

I typically provide a setter on my classes for configuring the log object. Hardly perfect, but it keeps the constructor signatures short. I am liking this suggestion to use a global variable though. I think I'll experiment.

Ok, you're using setter injection instead of constructor injection.

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?

Andrea

···

On Jun 25, 2008, at 11:04 AM, Andrea Fazzi wrote: