andreas1234567 has asked for the wisdom of the Perl Monks concerning the following question:

I've set up a simple Apache web server with Mason and mod_perl. The Mason code accesses a MySQL database through a dedicated perl module called "database.pm". Log::Log4perl is used for logging. I'm trying to log messages from the Mason component and the messages from the database module to two separate logfiles. When Log4perl is initialized and called directly, it all works fine. When is use a wrapper class (which in turn calls Log4perl), all messages seems to end up in one of the logfiles.

I don't think this is a hierarchy issue in since the two categories don't inherit each other.

What's wrong with my wrapper class "MyLogger"? Does anyone have a working wrapper class for Log::Log4perl?
package MyLogger; # Wrapper class for Log::Log4perl. use strict; use warnings; use fields; use Log::Log4perl; my $log; my $module_name; sub new { # initialize and return MyLogger object my MyLogger $self = shift; unless (ref $self) { $self = fields::new ($self); } $module_name = shift; if (!Log::Log4perl::initialized()) { Log::Log4perl->init("/opt/etc/log4perl.conf"); $Log::Log4perl::caller_depth = 1; } $log = Log::Log4perl->get_logger($module_name); return $self; } sub debug{ # log a single message my ($self, $logmsg) = @_; $log->debug($logmsg); } ...
Sample calling code that works (not using wrapper class: Messages appear as expected in "database.log" and "apache.log" according to given parameter):
Log::Log4perl->init("/opt/etc/log4perl.conf"); my $log = Log::Log4perl->get_logger("database"); ... $log->debug("blah blah");
Sample calling code that does *not* work (using wrapper class: All messages end up in "apache.log")
my $log MyLogger::->new("database"); ... $log->debug("blah blah");
The setup is as follows:
OS RH7.3 Linux 2.4.20-20.7 i686 Apache/1.3.23 (Unix) (Red-Hat/Linux) HTML::Mason INST_VERSION 1.23 mod_perl INST_VERSION 1.26 Log::Log4perl INST_VERSION 0.37
The configuration file /opt/etc/log4perl.conf:
log4perl.logger.database = DEBUG, FileAppenderDatabase log4perl.logger.apache = DEBUG, FileAppenderApache ### APPENDERS ### log4perl.appender.FileAppenderDatabase= Log::Log4perl::Appender::F +ile log4perl.appender.FileAppenderDatabase.filename=/var/log/database.log log4perl.appender.FileAppenderDatabase.mode= append log4perl.appender.FileAppenderApache= Log::Log4perl::Appender:: +File log4perl.appender.FileAppenderApache.filename= /var/log/apache.log log4perl.appender.FileAppenderApache.mode= append ### LAYOUTS ### log4perl.appender.FileAppenderDatabase.layout=PatternLayout log4perl.appender.FileAppenderDatabase.layout.ConversionPattern=%d %p> + %F:%L [%P] %M - %m%n log4perl.appender.FileAppenderApache.layout=PatternLayout log4perl.appender.FileAppenderApache.layout.ConversionPattern=%d %p> % +C %F:%L [%P] %M - %m%n
Update: Yes it's a typo, it should read "my $log =MyLogger::->new("database");"

Replies are listed 'Best First'.
Re: Wrapper class for log4perl redirects to wrong log file
by PodMaster (Abbot) on Feb 09, 2004 at 13:26 UTC
    my $log MyLogger::->new("database"); is either a typo or the source of your problems, i can't tell which (you're missing a "=").

    update: i've decided it's a typo :) and I've also decided that you should upgrade to the latest log4perl and that what you've posted pretty much works as it should, observe:

    C:\dev\LOOSE>perl my $linguini = ' log4perl.logger.database = DEBUG, FileAppenderDatabase log4perl.logger.apache = DEBUG, FileAppenderApache ### APPENDERS ### log4perl.appender.FileAppenderDatabase= Log::Log4perl::Appende +r::File log4perl.appender.FileAppenderDatabase.filename=./database.log log4perl.appender.FileAppenderDatabase.mode= append log4perl.appender.FileAppenderApache= Log::Log4perl::Appende +r::File log4perl.appender.FileAppenderApache.filename= ./apache.log log4perl.appender.FileAppenderApache.mode= append ### LAYOUTS ### log4perl.appender.FileAppenderDatabase.layout=PatternLayout log4perl.appender.FileAppenderDatabase.layout.ConversionPattern=%d %p> + %F:%L [%P] %M - %m%n log4perl.appender.FileAppenderApache.layout=PatternLayout log4perl.appender.FileAppenderApache.layout.ConversionPattern=%d %p> % +C %F:%L [%P] %M - %m%n '; package MyLogger; # Wrapper class for Log::Log4perl. { use strict; use warnings; use fields; use Log::Log4perl; my $log; my $module_name; sub new { # initialize and return MyLogger object my MyLogger $self = shift; unless (ref $self) { $self = fields::new ($self); } $module_name = shift; if (!Log::Log4perl::initialized()) { Log::Log4perl->init(\$linguini ); $Log::Log4perl::caller_depth = 1; } $log = Log::Log4perl->get_logger($module_name); return $self; } sub debug{ # log a single message my ($self, $logmsg) = @_; $log->debug($logmsg); } } #Log::Log4perl->init("/opt/etc/log4perl.conf"); #my $log = Log::Log4perl->get_logger("database"); #$log->debug("blah blah"); my $log = MyLogger::->new("database"); $log->debug("blah blah"); $log = MyLogger::->new("apache"); $log->debug("blih blih"); __END__ C:\dev\LOOSE>cat database.log 2004/02/09 05:54:18 DEBUG> log.log4perl.bug.pl:61 [908] main:: - blah +blah 2004/02/09 05:54:40 DEBUG> log.log4perl.bug.pl:60 [728] main:: - blah +blah 2004/02/09 05:56:06 DEBUG> -:60 [1316] main:: - blah blah C:\dev\LOOSE>cat apache.log 2004/02/09 05:54:18 DEBUG> MyLogger log.log4perl.bug.pl:63 [908] main: +: - blih blih 2004/02/09 05:54:40 DEBUG> MyLogger log.log4perl.bug.pl:62 [728] main: +: - blih blih 2004/02/09 05:56:06 DEBUG> MyLogger -:62 [1316] main:: - blih blih C:\dev\LOOSE>

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      PodMaster, I humbly thank you for your prompt reply.
      Module id = Log::Log4perl INST_VERSION 0.41
      Upgrading seems to have no impact on current issue.

      PodMaster: what you've posted pretty much works as it should

      I agree. I ran your code and it worked as expected. Except when it comes to Apache/mod_perl/mason components. Has anyone successfully used Log::Log4perl in a similar setting?

      index.mas:
      my $log = MyLogger::->new("apache"); $log->fatal("This should be in the APACHE log"); my $retval = database::do_some_logging(); $log->fatal("This should be in the APACHE log");
      database.pm:
      my $log = MyLogger::->new("database"); sub do_some_logging { $log->warn("This should appear in DATABASE log." +); }
      database.log:
      [empty]
      apache.log (edited for readability)
      FATAL> This should be in the APACHE log WARN> This should appear in DATABASE log. FATAL> This should be in the APACHE log
        I just ran it in a mason component without any issues (not that there should've been any). Try restarting apache, maybe something is getting cached or something something (works for me, i suggest you post on the mason/log4perl mailing lists).

        If setting up a singleton is all you were after (which apparently you were), log4perl already does that (which cees demonstrated).

        MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
        I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
        ** The third rule of perl club is a statement of fact: pod is sexy.

Re: Wrapper class for log4perl redirects to wrong log file
by cees (Curate) on Feb 09, 2004 at 16:41 UTC

    It looks like you are trying to setup an instance of Log::Log4perl up as a singleton (you are using my $log in the global scope of the module). Why bother with this when Log4perl already does this for you?

    I would probably forgo the creation of an object, and just provide a class function in your module that gets a Log4perl object and initializes it first if it isn't already initialized. Something like this:

    package MyLogger; # Wrapper class for Log::Log4perl. use strict; use warnings; use Log::Log4perl; sub get_logger { # initialize and return MyLogger object my $class = shift; my $module_name = shift; unless (Log::Log4perl->initialized) { Log::Log4perl->init("/opt/etc/log4perl.conf"); $Log::Log4perl::caller_depth = 1; } return Log::Log4perl->get_logger($module_name); }

    Then in your code you get to use the actual Log::Log4perl object and you won't have to write helper functions like 'debug' and such. It is still efficient, since Log::Log4perl is a singleton. Here is how you could use it:

    my $log = MyLogger->get_logger('database'); $log->debug('database blah blah'); $log = MyLogger->get_logger('apache'); $log->debug('apache blah blah');

    The above code is untested, but it should work...

    -Cees

      Exactly what I was looking for! Thanks a lot!

      Andreas

      --