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

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

Robert Dober

Robert Dober ha scritto:

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 alternat= ive
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.
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 ;)

41. A logging mixin

module MyLogger
logger =3D 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 =3D ...
logger2 =3D ...
class << self; self end.module_eval do
define_meyhod :included do |into_module|
logger =3D
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
--=20
http://ruby-smalltalk.blogspot.com/

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

Andrea Fazzi

Robert Dober ha scritto:
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 :)
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..
 
C

Chuck Remes

Robert Dober ha scritto:

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.

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
 
R

Robert Dober

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

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

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" ;) They too were taking
"off by one" goal;)

Robert
 
R

Robert Dober

:
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
--=20
http://ruby-smalltalk.blogspot.com/

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

Lars Christensen

A bit offtopic:

If Ruby handled modules more like namespaces in C++ or similar
languages, a case such as 'log' would be trivial:

module MyModule
def MyModule.log(a)
puts a
end
class MyClass
def func
log("event") # => NoMethodError: Ruby doesn't search "parent"
modules
end
end
end

Similarly, Ruby doesn't search for "class" method like other
languages:

class MyClass
def MyClass.log(a)
puts a
end
def func
log("event") # => NoMethoderror
end
end

I am interested in rationale for this behaviour in Ruby. The examples
are intuitive, to me at least, and I'm curious what the reason is that
Ruby doesn't search for method in the class/parent module when an
instance method or doesn't exist in the class or included Module.

Lars
 
A

Andrea Fazzi

Chuck Remes ha scritto:
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
 
R

Robert Klemme

2008/6/26 Robert Dober said:
I fail to understand Robert, look at this IRB session, as you see I
have prepared for your mail ;)

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. :)
And what do you say now about the "Mannschaft" ;) They too were taking
"off by one" goal;)

Um, yes. What an awful game.

Cheers

robert
 
I

Iñaki Baz Castillo

El Jueves, 26 de Junio de 2008, Shot (Piotr Szotkowski) escribi=C3=B3:
module MyProgram
=C2=A0 class Logger
=C2=A0 class << self
=C2=A0 =C2=A0 def debug =E2=80=A6
=C2=A0 =C2=A0 =C2=A0 =E2=80=A6
=C2=A0 =C2=A0 end
=C2=A0 end
=C2=A0 end
end

This way the global namespace is not cluttered, there are no conflicts
with other programs=E2=80=99 Logger classes/objects, while anywhere in yo= ur
program (i.e., anywhere in module MyProgram) you can simply call

Logger.debug =E2=80=A6

which is the clearest syntax to my eyes.

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

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

module MyProgram
class MyLogger
@@logger =3D Logger.new(xxxxx,xxxx)
class << self
def debug(text)
@@logger.debug(txt)
end
end
end
end


Is it ok?
Thanks a lot.


=2D-=20
I=C3=B1aki Baz Castillo
 
R

Robert Dober

I=F1aki Baz Castillo:



No =96 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
 
I

Iñaki Baz Castillo

El Viernes, 27 de Junio de 2008, Robert Dober escribi=F3:
grep --recursive '@@' lib && echo "Classvariables are the root of all evil
;)"

It's not the first time I read this, but could I know a good reason for not=
=20
using @@class variables?

Thanks.


=2D-=20
I=F1aki Baz Castillo
 
I

Iñaki Baz Castillo

El Viernes, 27 de Junio de 2008, Shot (Piotr Szotkowski) escribi=C3=B3:
I=C3=B1aki Baz Castillo:

No =E2=80=93 by using class << self, you=E2=80=99re operating on the Logg= er object
(an instance of the class Class), and you can access its *instance*
variables.=20

Thanks, in fact I didn't understand the meaning of "class << self" until no=
w.

That=E2=80=99s the other elegant part about this solution =E2=80=93 you c= an=20
use anything you=E2=80=99re used to, including, for example, attr_accesso= rs.

I use this approach like in the below code; I can use stuff like

$stderr.puts 'some debug line' if Config.debug

1.upto Config.max_pins do |pin|
=E2=80=A6
end

or decide whether to use Enumerable#map or the forkoff
gem based on whether Config.processes is one or more. :)

This seems really cool. I'll try to use it.

Thanks a lot.


=2D-=20
I=C3=B1aki Baz Castillo
 
I

Iñaki Baz Castillo

El Viernes, 27 de Junio de 2008, Shot (Piotr Szotkowski) escribi=C3=B3:
module ArtDecomp class Config

class << self

=C2=A0 attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone

=C2=A0 def init
=C2=A0 =C2=A0 @debug =C2=A0 =C2=A0 =3D false
=C2=A0 =C2=A0 @processes =3D 1
=C2=A0 =C2=A0 @qu_method =3D :graph_merger
=C2=A0 =C2=A0 @qv_method =3D :graph_merger
=C2=A0 =C2=A0 @silicone =C2=A0=3D Set[Arch[4,2], Arch[5,1]]
=C2=A0 end

=C2=A0 def log string, run, runs, dawn
=C2=A0 =C2=A0 left =3D ((Time.now - dawn)/run*(runs - run)).ceil
=C2=A0 =C2=A0 $stderr << " [#{string} #{runs - run} #{left}s] " if Config= =2Edebug
=C2=A0 end

=C2=A0 def max_pins
=C2=A0 =C2=A0 @silicone.map{|arch| arch.pins}.max
=C2=A0 end

=C2=A0 def max_pons
=C2=A0 =C2=A0 @silicone.map{|arch| arch.pons}.max
=C2=A0 end

=C2=A0 alias reset init

end

end end

ArtDecomp::Config.init


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

Thanks a lot.
=2D-=20
I=C3=B1aki Baz Castillo
 
R

Robert Dober

<snip>
Amen

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

Cheers
Robert


They are shared by all subclasses, look at this
irb(main):006:0> class A
irb(main):007:1> @@a =3D self
irb(main):008:1> end
=3D> A
irb(main):009:0> class B < A
irb(main):010:1> @@a =3D self
irb(main):011:1> end
=3D> B
irb(main):013:0> A.send :class_variable_get, :mad:@a
=3D> 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 =3D self
irb(main):016:1> end
=3D> A
irb(main):017:0> class B < A
irb(main):018:1> @a =3D self
irb(main):019:1> end
=3D> B
irb(main):020:0> A.instance_variable_get :mad:a
=3D> A
irb(main):021:0> B.instance_variable_get :mad:a
=3D> B

HTH
Robert




--=20
http://ruby-smalltalk.blogspot.com/
 
C

Chuck Remes

Chuck Remes ha scritto:

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?

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
 
A

ara.t.howard

El Viernes, 27 de Junio de 2008, Shot (Piotr Szotkowski) escribi=F3:
module ArtDecomp class Config
class << self
=20
attr_accessor :debug, :processes, :qu_method, :qv_method, :silicone
def init
@debug =3D false
@processes =3D 1
@qu_method =3D :graph_merger
@qv_method =3D :graph_merger
@silicone =3D Set[Arch[4,2], Arch[5,1]]
end =85
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. :)

I couldn=92t come up with any nice solution; note that you can =20
initialize
this class anywhere, so if you have some other place that does any =20
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.

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

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

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

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

a @ http://codeforpeople.com/
 
I

Iñaki Baz Castillo

El S=E1bado, 28 de Junio de 2008, ara.t.howard escribi=F3:
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 =3D false
@processes =3D 1
@qu_method =3D :graph_merger
@qv_method =3D :graph_merger
@silicone =3D 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.

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

=2D-=20
I=F1aki Baz Castillo
 
I

Iñaki Baz Castillo

El Domingo, 29 de Junio de 2008, Shot (Piotr Szotkowski) escribi=C3=B3:
ara.t.howard:

Ah, so perfect! Thanks a lot!

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


=2D-=20
I=C3=B1aki Baz Castillo
 
C

Chuck Remes

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
 

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