Mike Schilli wrote:
> Tom Metro wrote:
>> It came about because the $logger object doesn't actually convey the
>> full state, so when serialized and passed to a remote method, the
>> remote logging reverts to default logging settings.
> 
> Yeah, the way it's currently implemented, a logger is only meaningful
> within an initialized Log4perl-system, there's some system-global magic
> involved, and you cannot serialize a logger and plant it into
> another Log4perl-enabled system at runtime easily (yet).
> 
> Can you explain more about what the use case is for serializing loggers
> and passing them to remote systems?

Sure. This is part of a parallel processing cluster. Class::Remote is
used to execute code on remote nodes of the cluster. I'd like to be able
to have a single configured logger object used for the controlling node
(local) and then be able to serialize it and pass it to a remote node so
it can log with the identical settings.


> What properties of the logger might be of interest to the remote
> system?

The better questions is what logger state information can't practically
be serialized?


> Is the remote system running Log4perl with exactly the same appenders
> or different ones?

In this case I know the answer is exactly the same - everything is
outputting to STDOUT. But generally speaking, I'm not sure I know what
you mean.

Are you asking whether the remote system has the same appender drivers
installed? Or are you asking whether the remote system will be logging
to identical paths?

I don't think as a module implementer you need to be concerned with
that. If the paths are not identical, then having "fixup" code on the
remote side will be an obvious requirement. And similarly, an
experienced developer will know better than to write to a network
mounted log file from multiple machines simultaneously.


>> I assume that this practice of maintaining global state in Log4perl
>> came about because it is convenient to be able to call get_logger() as
>> a class method, rather than having the application pass an object
>> around.  But it seems that it would be way better if Log4perl was
>> internally designed to store all state in instance variables, and
>> leave it to the application developer to decide whether they want to
>> pass around objects, or dedicate a global variable to it.
> 
> Agreed.

OK, so what can we do to start refactoring the code to have the more
desired architecture?


>> Ultimately I want a method I can call from my libraries where I can
>> pass in a $logger parameter. The method then looks to see if $logger
>> is set to anything, and if it is, clones it, sets the category, and
>> returns the object.
> 
> Hmm, that's similar to how the class method is defined. If the logger
> for the category exists, you get a copy, if it isn't, you get a new
> instance. We could implement an object method that does what you've
> suggested, can you post some code to display how your class hierarchy
> looks like and how you call the metods of the derived class from your
> application?

Sure.

By illustration, here's a sticky situation I'm dealing with right now.

In the controller, I have code approximately like:

  my $logger = Publisher::Logger->init;
  ...
  my $parallel_agents = Parallel::Cluster->new({
      agent_lib => 'Some::Agent::Class',
      logger    => $logger,
  });
  $parallel_agents->run();

This initializes a logger object, instantiates the parallel processing
system, passing in the logger object, which in turn will get passed to
the constructor of Some::Agent::Class, and kicks off the run.

Parallel::Cluster implements a map-reduce algorithm, where portions
(some instances of Some::Agent::Class) are executed locally on the
controlling node, while other portions (mapping) happen remotely via
Class::Remote.

If the agent code attempts to use $logger locally, it works as expected.
If they use it remotely, nothing gets output.

Looking at a dump of the $logger object shows little difference between
the working local version and the not working remote version:

local
Address: Publisher::Logger=HASH(0x41f9dc0)@15665
$logger = bless( {
                   'is_OFF' => sub { "DUMMY" },
                   'is_DEBUG' => sub { "DUMMY" },
                   'ERROR' => sub { "DUMMY" },
                   'is_INFO' => sub { "DUMMY" },
                   'layout' => undef,
                   'category' => 'main',
                   'is_TRACE' => sub { "DUMMY" },
                   'DEBUG' => $logger->{'ERROR'},
                   'is_ALL' => sub { "DUMMY" },
                   'additivity' => 1,
                   'TRACE' => sub { "DUMMY" },
                   'ALL' => $logger->{'TRACE'},
                   'is_FATAL' => sub { "DUMMY" },
                   'is_WARN' => sub { "DUMMY" },
                   'FATAL' => $logger->{'ERROR'},
                   'appender_names' => [
                                         'app001'
                                       ],
                   'WARN' => $logger->{'ERROR'},
                   'INFO' => $logger->{'ERROR'},
                   'level' => 10000,
                   'num_appenders' => 1,
                   'OFF' => $logger->{'ERROR'},
                   'is_ERROR' => sub { "DUMMY" }
                 }, 'Publisher::Logger' );


remote
Address: Publisher::Logger=HASH(0x411caa0)@15673
$logger = bless( {
                   'is_OFF' => sub { "DUMMY" },
                   'is_DEBUG' => sub { "DUMMY" },
                   'ERROR' => sub { "DUMMY" },
                   'layout' => undef,
                   'is_INFO' => sub { "DUMMY" },
                   'category' => 'main',
                   'DEBUG' => $logger->{'ERROR'},
                   'is_TRACE' => sub { "DUMMY" },
                   'TRACE' => sub { "DUMMY" },
                   'additivity' => 1,
                   'is_ALL' => sub { "DUMMY" },
                   'ALL' => $logger->{'TRACE'},
                   'is_FATAL' => sub { "DUMMY" },
                   'is_WARN' => sub { "DUMMY" },
                   'INFO' => $logger->{'ERROR'},
                   'WARN' => $logger->{'ERROR'},
                   'appender_names' => [
                                         'app001'
                                       ],
                   'FATAL' => $logger->{'ERROR'},
                   'level' => '10000',
                   'num_appenders' => 1,
                   'is_ERROR' => sub { "DUMMY" },
                   'OFF' => $logger->{'ERROR'}
                 }, 'Publisher::Logger' );


To compensate for this deficiency, I added code to the agent to
redundantly call Publisher::Logger->init, and this redundancy is
primarily why I spun off logger initialization to subclass of
Log::Log4perl::Logger.

However this then introduced a bug. The place in the agent class where
the logger gets initialized (in its constructor) is called both when the
agent is instantiated for local portions as well as remote portions. As
a result, it re-initalized the state of the local logger object, and as
I reported back in November when I ran across a similar bug I created,
it has the unexpected effect of killing all logged output in the local
process.

So I had to modify init() in my subclass to do:

    # bail out and return the global logger object if already initialized
    if ($Log::Log4perl::Logger::INITIALIZED) {
        return $class->get_logger($caller_class);
    }

And that fixed it.


But weighing the pros and cons...

Current scheme:

Pro: have the convenience of calling $class->get_logger() anywhere to
get a logger;

Con: all other behavior is less intuitive; loggers in the same memory
space can interact and library code, if not well behaved, can kill your
logger object; you can't serialize a logger object.

A revised scheme:

Pro: none of the cons above;

Con: you have to handle passing around your own $logger object, or
setting a global.

I'll take the latter any day.

 -Tom

------------------------------------------------------------------------------
Special Offer-- Download ArcSight Logger for FREE (a $49 USD value)!
Finally, a world-class log management solution at an even better price-free!
Download using promo code Free_Logger_4_Dev2Dev. Offer expires 
February 28th, so secure your free ArcSight Logger TODAY! 
http://p.sf.net/sfu/arcsight-sfd2d
_______________________________________________
log4perl-devel mailing list
log4perl-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/log4perl-devel

Reply via email to