Is there a way to use Log::Log4perl to make a smart self-logging module that logs its operations to a file even in the absence of the calling script not initializing Log4perl? As far as I can tell from the documentation, the only way to use Log4perl is to initialize it in the running script from a configuration, then modules implementing Log4perl calls log themselves based on the caller's Log4perl config.
Instead, I'd like the modules to provide a default initialization config for Log4perl. This would provide the default file appender for the module's category. Then, I could override this behavior by initing Log4perl in the caller with a different config if needed, and everything would hopefully just work.
Is this sort of defensive logging behavior possible or am I going to need to rely on initing Log4perl in every .pl script that calls the module I want logged?
Log::Log4perl is a Perl port of the widely popular log4j logging package. Logging beats a debugger if you want to know what's going on in your code during runtime. However, traditional logging packages are too static and generate a flood of log messages in your log files that won't help you. Log::Log4perl is different.
The responsibility to set up the logging lies with the script launching it. I think this is a better approach then letting the module initialize the logger. After all, modules won't run on their own.
In the most simple case we use it to add logging messages to a script. We need to load the module importing the :easy key that will import 6 functions for the 6 logging levels provided by Log::Log4perl and 6 variables with corresponding names.
in order to have the logger object. There is a way to reduce the typing, we can import the get_loggermethod from Log::Log4perl. We can then write: use Log::Log4perl qw(get_logger); sub run { my $logger = get_logger(); $logger->fatal("FATAL from EasyApp"); $logger->debug("DEBUG from EasyApp"); }
I do this in a custom Log role in Moose (irrelevant complicated code removed):
package MyApp::Role::Log;
use Moose::Role;
use Log::Log4perl;
my @methods = qw(
log trace debug info warn error fatal
is_trace is_debug is_info is_warn is_error is_fatal
logexit logwarn error_warn logdie error_die
logcarp logcluck logcroak logconfess
);
has _logger => (
is => 'ro',
isa => 'Log::Log4perl::Logger',
lazy_build => 1,
handles => \@methods,
);
around $_ => sub {
my $orig = shift;
my $this = shift;
# one level for this method itself
# two levels for Class:;MOP::Method::Wrapped (the "around" wrapper)
# one level for Moose::Meta::Method::Delegation (the "handles" wrapper)
local $Log::Log4perl::caller_depth;
$Log::Log4perl::caller_depth += 4;
my $return = $this->$orig(@_);
$Log::Log4perl::caller_depth -= 4;
return $return;
} foreach @methods;
method _build__logger => sub {
my $this = shift;
my $loggerName = ref($this);
Log::Log4perl->easy_init() if not Log::Log4perl::initialized();
return Log::Log4perl->get_logger($loggerName)
};
As you can see, the log object is self-initializing -- if Log::Log4perl->init
has not been called, then easy_init
is called. You could easily modify this to allow each module to customize its logger -- I do so with optional role parameters, with ref($this)
as the default fallback.
PS. You may also want to look at MooseX::Log::Log4perl, which is where I started before I used the logger role above. Someday when I get around to it I will submit some much-needed patches to that MX module to incorporate some features I have added.
The short answer is to call Log::Log4perl::initialized(); at some point and if it is false, set up some default logging.
The tricky part is "some point."
You can't do it in BEGIN {}, because then the main script's will stomp your initialisation even though you created unnessary files. You don't want to do it before each call to get_logger() because that is wasteful. So you should do it during the module's initialisation, say sub new or sub init.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With