diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2014-11-01 01:47:12 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2014-11-01 01:47:12 +0000 |
commit | 94566f012421026c8311552f99175a5989eba063 (patch) | |
tree | 0bfd47111b94a1715d14b8c4ab2d82ad1abc09b1 /lib/Log/Log4perl.pm | |
download | Log-Log4perl-tarball-master.tar.gz |
Log-Log4perl-1.46HEADLog-Log4perl-1.46master
Diffstat (limited to 'lib/Log/Log4perl.pm')
-rw-r--r-- | lib/Log/Log4perl.pm | 2956 |
1 files changed, 2956 insertions, 0 deletions
diff --git a/lib/Log/Log4perl.pm b/lib/Log/Log4perl.pm new file mode 100644 index 0000000..6568184 --- /dev/null +++ b/lib/Log/Log4perl.pm @@ -0,0 +1,2956 @@ +################################################## +package Log::Log4perl; +################################################## + +END { local($?); Log::Log4perl::Logger::cleanup(); } + +use 5.006; +use strict; +use warnings; + +use Log::Log4perl::Util; +use Log::Log4perl::Logger; +use Log::Log4perl::Level; +use Log::Log4perl::Config; +use Log::Log4perl::Appender; + +our $VERSION = '1.46'; + + # set this to '1' if you're using a wrapper + # around Log::Log4perl +our $caller_depth = 0; + + #this is a mapping of convenience names to opcode masks used in + #$ALLOWED_CODE_OPS_IN_CONFIG_FILE below +our %ALLOWED_CODE_OPS = ( + 'safe' => [ ':browse' ], + 'restrictive' => [ ':default' ], +); + +our %WRAPPERS_REGISTERED = map { $_ => 1 } qw(Log::Log4perl); + + #set this to the opcodes which are allowed when + #$ALLOW_CODE_IN_CONFIG_FILE is set to a true value + #if undefined, there are no restrictions on code that can be + #excuted +our @ALLOWED_CODE_OPS_IN_CONFIG_FILE; + + #this hash lists things that should be exported into the Safe + #compartment. The keys are the package the symbol should be + #exported from and the values are array references to the names + #of the symbols (including the leading type specifier) +our %VARS_SHARED_WITH_SAFE_COMPARTMENT = ( + main => [ '%ENV' ], +); + + #setting this to a true value will allow Perl code to be executed + #within the config file. It works in conjunction with + #$ALLOWED_CODE_OPS_IN_CONFIG_FILE, which if defined restricts the + #opcodes which can be executed using the 'Safe' module. + #setting this to a false value disables code execution in the + #config file +our $ALLOW_CODE_IN_CONFIG_FILE = 1; + + #arrays in a log message will be joined using this character, + #see Log::Log4perl::Appender::DBI +our $JOIN_MSG_ARRAY_CHAR = ''; + + #version required for XML::DOM, to enable XML Config parsing + #and XML Config unit tests +our $DOM_VERSION_REQUIRED = '1.29'; + +our $CHATTY_DESTROY_METHODS = 0; + +our $LOGDIE_MESSAGE_ON_STDERR = 1; +our $LOGEXIT_CODE = 1; +our %IMPORT_CALLED; + +our $EASY_CLOSURES = {}; + + # to throw refs as exceptions via logcarp/confess, turn this off +our $STRINGIFY_DIE_MESSAGE = 1; + +use constant _INTERNAL_DEBUG => 0; + +################################################## +sub import { +################################################## + my($class) = shift; + + my $caller_pkg = caller(); + + return 1 if $IMPORT_CALLED{$caller_pkg}++; + + my(%tags) = map { $_ => 1 } @_; + + # Lazy man's logger + if(exists $tags{':easy'}) { + $tags{':levels'} = 1; + $tags{':nowarn'} = 1; + $tags{'get_logger'} = 1; + } + + if(exists $tags{':no_extra_logdie_message'}) { + $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR = 0; + delete $tags{':no_extra_logdie_message'}; + } + + if(exists $tags{get_logger}) { + # Export get_logger into the calling module's + no strict qw(refs); + *{"$caller_pkg\::get_logger"} = *get_logger; + + delete $tags{get_logger}; + } + + if(exists $tags{':levels'}) { + # Export log levels ($DEBUG, $INFO etc.) from Log4perl::Level + for my $key (keys %Log::Log4perl::Level::PRIORITY) { + my $name = "$caller_pkg\::$key"; + # Need to split this up in two lines, or CVS will + # mess it up. + my $value = $ + Log::Log4perl::Level::PRIORITY{$key}; + no strict qw(refs); + *{"$name"} = \$value; + } + + delete $tags{':levels'}; + } + + # Lazy man's logger + if(exists $tags{':easy'}) { + delete $tags{':easy'}; + + # Define default logger object in caller's package + my $logger = get_logger("$caller_pkg"); + + # Define DEBUG, INFO, etc. routines in caller's package + for(qw(TRACE DEBUG INFO WARN ERROR FATAL ALWAYS)) { + my $level = $_; + $level = "OFF" if $level eq "ALWAYS"; + my $lclevel = lc($_); + easy_closure_create($caller_pkg, $_, sub { + Log::Log4perl::Logger::init_warn() unless + $Log::Log4perl::Logger::INITIALIZED or + $Log::Log4perl::Logger::NON_INIT_WARNED; + $logger->{$level}->($logger, @_, $level); + }, $logger); + } + + # Define LOGCROAK, LOGCLUCK, etc. routines in caller's package + for(qw(LOGCROAK LOGCLUCK LOGCARP LOGCONFESS)) { + my $method = "Log::Log4perl::Logger::" . lc($_); + + easy_closure_create($caller_pkg, $_, sub { + unshift @_, $logger; + goto &$method; + }, $logger); + } + + # Define LOGDIE, LOGWARN + easy_closure_create($caller_pkg, "LOGDIE", sub { + Log::Log4perl::Logger::init_warn() unless + $Log::Log4perl::Logger::INITIALIZED or + $Log::Log4perl::Logger::NON_INIT_WARNED; + $logger->{FATAL}->($logger, @_, "FATAL"); + $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR ? + CORE::die(Log::Log4perl::Logger::callerline(join '', @_)) : + exit $Log::Log4perl::LOGEXIT_CODE; + }, $logger); + + easy_closure_create($caller_pkg, "LOGEXIT", sub { + Log::Log4perl::Logger::init_warn() unless + $Log::Log4perl::Logger::INITIALIZED or + $Log::Log4perl::Logger::NON_INIT_WARNED; + $logger->{FATAL}->($logger, @_, "FATAL"); + exit $Log::Log4perl::LOGEXIT_CODE; + }, $logger); + + easy_closure_create($caller_pkg, "LOGWARN", sub { + Log::Log4perl::Logger::init_warn() unless + $Log::Log4perl::Logger::INITIALIZED or + $Log::Log4perl::Logger::NON_INIT_WARNED; + $logger->{WARN}->($logger, @_, "WARN"); + CORE::warn(Log::Log4perl::Logger::callerline(join '', @_)) + if $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR; + }, $logger); + } + + if(exists $tags{':nowarn'}) { + $Log::Log4perl::Logger::NON_INIT_WARNED = 1; + delete $tags{':nowarn'}; + } + + if(exists $tags{':nostrict'}) { + $Log::Log4perl::Logger::NO_STRICT = 1; + delete $tags{':nostrict'}; + } + + if(exists $tags{':resurrect'}) { + my $FILTER_MODULE = "Filter::Util::Call"; + if(! Log::Log4perl::Util::module_available($FILTER_MODULE)) { + die "$FILTER_MODULE required with :resurrect" . + "(install from CPAN)"; + } + eval "require $FILTER_MODULE" or die "Cannot pull in $FILTER_MODULE"; + Filter::Util::Call::filter_add( + sub { + my($status); + s/^\s*###l4p// if + ($status = Filter::Util::Call::filter_read()) > 0; + $status; + }); + delete $tags{':resurrect'}; + } + + if(keys %tags) { + # We received an Option we couldn't understand. + die "Unknown Option(s): @{[keys %tags]}"; + } +} + +################################################## +sub initialized { +################################################## + return $Log::Log4perl::Logger::INITIALIZED; +} + +################################################## +sub new { +################################################## + die "THIS CLASS ISN'T FOR DIRECT USE. " . + "PLEASE CHECK 'perldoc " . __PACKAGE__ . "'."; +} + +################################################## +sub reset { # Mainly for debugging/testing +################################################## + # Delegate this to the logger ... + return Log::Log4perl::Logger->reset(); +} + +################################################## +sub init_once { # Call init only if it hasn't been + # called yet. +################################################## + init(@_) unless $Log::Log4perl::Logger::INITIALIZED; +} + +################################################## +sub init { # Read the config file +################################################## + my($class, @args) = @_; + + #woops, they called ::init instead of ->init, let's be forgiving + if ($class ne __PACKAGE__) { + unshift(@args, $class); + } + + # Delegate this to the config module + return Log::Log4perl::Config->init(@args); +} + +################################################## +sub init_and_watch { +################################################## + my($class, @args) = @_; + + #woops, they called ::init instead of ->init, let's be forgiving + if ($class ne __PACKAGE__) { + unshift(@args, $class); + } + + # Delegate this to the config module + return Log::Log4perl::Config->init_and_watch(@args); +} + + +################################################## +sub easy_init { # Initialize the root logger with a screen appender +################################################## + my($class, @args) = @_; + + # Did somebody call us with Log::Log4perl::easy_init()? + if(ref($class) or $class =~ /^\d+$/) { + unshift @args, $class; + } + + # Reset everything first + Log::Log4perl->reset(); + + my @loggers = (); + + my %default = ( level => $DEBUG, + file => "STDERR", + utf8 => undef, + category => "", + layout => "%d %m%n", + ); + + if(!@args) { + push @loggers, \%default; + } else { + for my $arg (@args) { + if($arg =~ /^\d+$/) { + my %logger = (%default, level => $arg); + push @loggers, \%logger; + } elsif(ref($arg) eq "HASH") { + my %logger = (%default, %$arg); + push @loggers, \%logger; + } + } + } + + for my $logger (@loggers) { + + my $app; + + if($logger->{file} =~ /^stderr$/i) { + $app = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::Screen", + utf8 => $logger->{utf8}); + } elsif($logger->{file} =~ /^stdout$/i) { + $app = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::Screen", + stderr => 0, + utf8 => $logger->{utf8}); + } else { + my $binmode; + if($logger->{file} =~ s/^(:.*?)>/>/) { + $binmode = $1; + } + $logger->{file} =~ /^(>)?(>)?/; + my $mode = ($2 ? "append" : "write"); + $logger->{file} =~ s/.*>+\s*//g; + $app = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::File", + filename => $logger->{file}, + mode => $mode, + utf8 => $logger->{utf8}, + binmode => $binmode, + ); + } + + my $layout = Log::Log4perl::Layout::PatternLayout->new( + $logger->{layout}); + $app->layout($layout); + + my $log = Log::Log4perl->get_logger($logger->{category}); + $log->level($logger->{level}); + $log->add_appender($app); + } + + $Log::Log4perl::Logger::INITIALIZED = 1; +} + +################################################## +sub wrapper_register { +################################################## + my $wrapper = $_[-1]; + + $WRAPPERS_REGISTERED{ $wrapper } = 1; +} + +################################################## +sub get_logger { # Get an instance (shortcut) +################################################## + # get_logger() can be called in the following ways: + # + # (1) Log::Log4perl::get_logger() => () + # (2) Log::Log4perl->get_logger() => ("Log::Log4perl") + # (3) Log::Log4perl::get_logger($cat) => ($cat) + # + # (5) Log::Log4perl->get_logger($cat) => ("Log::Log4perl", $cat) + # (6) L4pSubclass->get_logger($cat) => ("L4pSubclass", $cat) + + # Note that (4) L4pSubclass->get_logger() => ("L4pSubclass") + # is indistinguishable from (3) and therefore can't be allowed. + # Wrapper classes always have to specify the category explicitly. + + my $category; + + if(@_ == 0) { + # 1 + my $level = 0; + do { $category = scalar caller($level++); + } while exists $WRAPPERS_REGISTERED{ $category }; + + } elsif(@_ == 1) { + # 2, 3 + $category = $_[0]; + + my $level = 0; + while(exists $WRAPPERS_REGISTERED{ $category }) { + $category = scalar caller($level++); + } + + } else { + # 5, 6 + $category = $_[1]; + } + + # Delegate this to the logger module + return Log::Log4perl::Logger->get_logger($category); +} + +########################################### +sub caller_depth_offset { +########################################### + my( $level ) = @_; + + my $category; + + { + my $category = scalar caller($level + 1); + + if(defined $category and + exists $WRAPPERS_REGISTERED{ $category }) { + $level++; + redo; + } + } + + return $level; +} + +################################################## +sub appenders { # Get a hashref of all defined appender wrappers +################################################## + return \%Log::Log4perl::Logger::APPENDER_BY_NAME; +} + +################################################## +sub add_appender { # Add an appender to the system, but don't assign + # it to a logger yet +################################################## + my($class, $appender) = @_; + + my $name = $appender->name(); + die "Mandatory parameter 'name' missing in appender" unless defined $name; + + # Make it known by name in the Log4perl universe + # (so that composite appenders can find it) + Log::Log4perl->appenders()->{ $name } = $appender; +} + +################################################## +# Return number of appenders changed +sub appender_thresholds_adjust { # Readjust appender thresholds +################################################## + # If someone calls L4p-> and not L4p:: + shift if $_[0] eq __PACKAGE__; + my($delta, $appenders) = @_; + my $retval = 0; + + if($delta == 0) { + # Nothing to do, no delta given. + return; + } + + if(defined $appenders) { + # Map names to objects + $appenders = [map { + die "Unkown appender: '$_'" unless exists + $Log::Log4perl::Logger::APPENDER_BY_NAME{ + $_}; + $Log::Log4perl::Logger::APPENDER_BY_NAME{ + $_} + } @$appenders]; + } else { + # Just hand over all known appenders + $appenders = [values %{Log::Log4perl::appenders()}] unless + defined $appenders; + } + + # Change all appender thresholds; + foreach my $app (@$appenders) { + my $old_thres = $app->threshold(); + my $new_thres; + if($delta > 0) { + $new_thres = Log::Log4perl::Level::get_higher_level( + $old_thres, $delta); + } else { + $new_thres = Log::Log4perl::Level::get_lower_level( + $old_thres, -$delta); + } + + ++$retval if ($app->threshold($new_thres) == $new_thres); + } + return $retval; +} + +################################################## +sub appender_by_name { # Get a (real) appender by name +################################################## + # If someone calls L4p->appender_by_name and not L4p::appender_by_name + shift if $_[0] eq __PACKAGE__; + + my($name) = @_; + + if(defined $name and + exists $Log::Log4perl::Logger::APPENDER_BY_NAME{ + $name}) { + return $Log::Log4perl::Logger::APPENDER_BY_NAME{ + $name}->{appender}; + } else { + return undef; + } +} + +################################################## +sub eradicate_appender { # Remove an appender from the system +################################################## + # If someone calls L4p->... and not L4p::... + shift if $_[0] eq __PACKAGE__; + Log::Log4perl::Logger->eradicate_appender(@_); +} + +################################################## +sub infiltrate_lwp { # +################################################## + no warnings qw(redefine); + + my $l4p_wrapper = sub { + my($prio, @message) = @_; + local $Log::Log4perl::caller_depth = + $Log::Log4perl::caller_depth + 2; + get_logger(scalar caller(1))->log($prio, @message); + }; + + *LWP::Debug::trace = sub { + $l4p_wrapper->($INFO, @_); + }; + *LWP::Debug::conns = + *LWP::Debug::debug = sub { + $l4p_wrapper->($DEBUG, @_); + }; +} + +################################################## +sub easy_closure_create { +################################################## + my($caller_pkg, $entry, $code, $logger) = @_; + + no strict 'refs'; + + print("easy_closure: Setting shortcut $caller_pkg\::$entry ", + "(logger=$logger\n") if _INTERNAL_DEBUG; + + $EASY_CLOSURES->{ $caller_pkg }->{ $entry } = $logger; + *{"$caller_pkg\::$entry"} = $code; +} + +########################################### +sub easy_closure_cleanup { +########################################### + my($caller_pkg, $entry) = @_; + + no warnings 'redefine'; + no strict 'refs'; + + my $logger = $EASY_CLOSURES->{ $caller_pkg }->{ $entry }; + + print("easy_closure: Nuking easy shortcut $caller_pkg\::$entry ", + "(logger=$logger\n") if _INTERNAL_DEBUG; + + *{"$caller_pkg\::$entry"} = sub { }; + delete $EASY_CLOSURES->{ $caller_pkg }->{ $entry }; +} + +################################################## +sub easy_closure_category_cleanup { +################################################## + my($caller_pkg) = @_; + + if(! exists $EASY_CLOSURES->{ $caller_pkg } ) { + return 1; + } + + for my $entry ( keys %{ $EASY_CLOSURES->{ $caller_pkg } } ) { + easy_closure_cleanup( $caller_pkg, $entry ); + } + + delete $EASY_CLOSURES->{ $caller_pkg }; +} + +########################################### +sub easy_closure_global_cleanup { +########################################### + + for my $caller_pkg ( keys %$EASY_CLOSURES ) { + easy_closure_category_cleanup( $caller_pkg ); + } +} + +########################################### +sub easy_closure_logger_remove { +########################################### + my($class, $logger) = @_; + + PKG: for my $caller_pkg ( keys %$EASY_CLOSURES ) { + for my $entry ( keys %{ $EASY_CLOSURES->{ $caller_pkg } } ) { + if( $logger == $EASY_CLOSURES->{ $caller_pkg }->{ $entry } ) { + easy_closure_category_cleanup( $caller_pkg ); + next PKG; + } + } + } +} + +################################################## +sub remove_logger { +################################################## + my ($class, $logger) = @_; + + # Any stealth logger convenience function still using it will + # now become a no-op. + Log::Log4perl->easy_closure_logger_remove( $logger ); + + # Remove the logger from the system + delete $Log::Log4perl::Logger::LOGGERS_BY_NAME->{ $logger->{category} }; +} + +1; + +__END__ + +=encoding utf8 + +=head1 NAME + +Log::Log4perl - Log4j implementation for Perl + +=head1 SYNOPSIS + # Easy mode if you like it simple ... + + use Log::Log4perl qw(:easy); + Log::Log4perl->easy_init($ERROR); + + DEBUG "This doesn't go anywhere"; + ERROR "This gets logged"; + + # ... or standard mode for more features: + + Log::Log4perl::init('/etc/log4perl.conf'); + + --or-- + + # Check config every 10 secs + Log::Log4perl::init_and_watch('/etc/log4perl.conf',10); + + --then-- + + $logger = Log::Log4perl->get_logger('house.bedrm.desk.topdrwr'); + + $logger->debug('this is a debug message'); + $logger->info('this is an info message'); + $logger->warn('etc'); + $logger->error('..'); + $logger->fatal('..'); + + #####/etc/log4perl.conf############################### + log4perl.logger.house = WARN, FileAppndr1 + log4perl.logger.house.bedroom.desk = DEBUG, FileAppndr1 + + log4perl.appender.FileAppndr1 = Log::Log4perl::Appender::File + log4perl.appender.FileAppndr1.filename = desk.log + log4perl.appender.FileAppndr1.layout = \ + Log::Log4perl::Layout::SimpleLayout + ###################################################### + +=head1 ABSTRACT + +Log::Log4perl provides a powerful logging API for your application + +=head1 DESCRIPTION + +Log::Log4perl lets you remote-control and fine-tune the logging behaviour +of your system from the outside. It implements the widely popular +(Java-based) Log4j logging package in pure Perl. + +B<For a detailed tutorial on Log::Log4perl usage, please read> + +L<http://www.perl.com/pub/a/2002/09/11/log4perl.html> + +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. + +C<Log::Log4perl> is different. It allows you to control the number of +logging messages generated at three different levels: + +=over 4 + +=item * + +At a central location in your system (either in a configuration file or +in the startup code) you specify I<which components> (classes, functions) +of your system should generate logs. + +=item * + +You specify how detailed the logging of these components should be by +specifying logging I<levels>. + +=item * + +You also specify which so-called I<appenders> you want to feed your +log messages to ("Print it to the screen and also append it to /tmp/my.log") +and which format ("Write the date first, then the file name and line +number, and then the log message") they should be in. + +=back + +This is a very powerful and flexible mechanism. You can turn on and off +your logs at any time, specify the level of detail and make that +dependent on the subsystem that's currently executed. + +Let me give you an example: You might +find out that your system has a problem in the +C<MySystem::Helpers::ScanDir> +component. Turning on detailed debugging logs all over the system would +generate a flood of useless log messages and bog your system down beyond +recognition. With C<Log::Log4perl>, however, you can tell the system: +"Continue to log only severe errors to the log file. Open a second +log file, turn on full debug logs in the C<MySystem::Helpers::ScanDir> +component and dump all messages originating from there into the new +log file". And all this is possible by just changing the parameters +in a configuration file, which your system can re-read even +while it's running! + +=head1 How to use it + +The C<Log::Log4perl> package can be initialized in two ways: Either +via Perl commands or via a C<log4j>-style configuration file. + +=head2 Initialize via a configuration file + +This is the easiest way to prepare your system for using +C<Log::Log4perl>. Use a configuration file like this: + + ############################################################ + # A simple root logger with a Log::Log4perl::Appender::File + # file appender in Perl. + ############################################################ + log4perl.rootLogger=ERROR, LOGFILE + + log4perl.appender.LOGFILE=Log::Log4perl::Appender::File + log4perl.appender.LOGFILE.filename=/var/log/myerrs.log + log4perl.appender.LOGFILE.mode=append + + log4perl.appender.LOGFILE.layout=PatternLayout + log4perl.appender.LOGFILE.layout.ConversionPattern=[%r] %F %L %c - %m%n + +These lines define your standard logger that's appending severe +errors to C</var/log/myerrs.log>, using the format + + [millisecs] source-filename line-number class - message newline + +Assuming that this configuration file is saved as C<log.conf>, you need to +read it in the startup section of your code, using the following +commands: + + use Log::Log4perl; + Log::Log4perl->init("log.conf"); + +After that's done I<somewhere> in the code, you can retrieve +logger objects I<anywhere> in the code. Note that +there's no need to carry any logger references around with your +functions and methods. You can get a logger anytime via a singleton +mechanism: + + package My::MegaPackage; + use Log::Log4perl; + + sub some_method { + my($param) = @_; + + my $log = Log::Log4perl->get_logger("My::MegaPackage"); + + $log->debug("Debug message"); + $log->info("Info message"); + $log->error("Error message"); + + ... + } + +With the configuration file above, C<Log::Log4perl> will write +"Error message" to the specified log file, but won't do anything for +the C<debug()> and C<info()> calls, because the log level has been set +to C<ERROR> for all components in the first line of +configuration file shown above. + +Why C<Log::Log4perl-E<gt>get_logger> and +not C<Log::Log4perl-E<gt>new>? We don't want to create a new +object every time. Usually in OO-Programming, you create an object +once and use the reference to it to call its methods. However, +this requires that you pass around the object to all functions +and the last thing we want is pollute each and every function/method +we're using with a handle to the C<Logger>: + + sub function { # Brrrr!! + my($logger, $some, $other, $parameters) = @_; + } + +Instead, if a function/method wants a reference to the logger, it +just calls the Logger's static C<get_logger($category)> method to obtain +a reference to the I<one and only> possible logger object of +a certain category. +That's called a I<singleton> if you're a Gamma fan. + +How does the logger know +which messages it is supposed to log and which ones to suppress? +C<Log::Log4perl> works with inheritance: The config file above didn't +specify anything about C<My::MegaPackage>. +And yet, we've defined a logger of the category +C<My::MegaPackage>. +In this case, C<Log::Log4perl> will walk up the namespace hierarchy +(C<My> and then we're at the root) to figure out if a log level is +defined somewhere. In the case above, the log level at the root +(root I<always> defines a log level, but not necessarily an appender) +defines that +the log level is supposed to be C<ERROR> -- meaning that I<DEBUG> +and I<INFO> messages are suppressed. Note that this 'inheritance' is +unrelated to Perl's class inheritance, it is merely related to the +logger namespace. +By the way, if you're ever in doubt about what a logger's category is, +use C<$logger-E<gt>category()> to retrieve it. + +=head2 Log Levels + +There are six predefined log levels: C<FATAL>, C<ERROR>, C<WARN>, C<INFO>, +C<DEBUG>, and C<TRACE> (in descending priority). Your configured logging level +has to at least match the priority of the logging message. + +If your configured logging level is C<WARN>, then messages logged +with C<info()>, C<debug()>, and C<trace()> will be suppressed. +C<fatal()>, C<error()> and C<warn()> will make their way through, +because their priority is higher or equal than the configured setting. + +Instead of calling the methods + + $logger->trace("..."); # Log a trace message + $logger->debug("..."); # Log a debug message + $logger->info("..."); # Log a info message + $logger->warn("..."); # Log a warn message + $logger->error("..."); # Log a error message + $logger->fatal("..."); # Log a fatal message + +you could also call the C<log()> method with the appropriate level +using the constants defined in C<Log::Log4perl::Level>: + + use Log::Log4perl::Level; + + $logger->log($TRACE, "..."); + $logger->log($DEBUG, "..."); + $logger->log($INFO, "..."); + $logger->log($WARN, "..."); + $logger->log($ERROR, "..."); + $logger->log($FATAL, "..."); + +This form is rarely used, but it comes in handy if you want to log +at different levels depending on an exit code of a function: + + $logger->log( $exit_level{ $rc }, "..."); + +As for needing more logging levels than these predefined ones: It's +usually best to steer your logging behaviour via the category +mechanism instead. + +If you need to find out if the currently configured logging +level would allow a logger's logging statement to go through, use the +logger's C<is_I<level>()> methods: + + $logger->is_trace() # True if trace messages would go through + $logger->is_debug() # True if debug messages would go through + $logger->is_info() # True if info messages would go through + $logger->is_warn() # True if warn messages would go through + $logger->is_error() # True if error messages would go through + $logger->is_fatal() # True if fatal messages would go through + +Example: C<$logger-E<gt>is_warn()> returns true if the logger's current +level, as derived from either the logger's category (or, in absence of +that, one of the logger's parent's level setting) is +C<$WARN>, C<$ERROR> or C<$FATAL>. + +Also available are a series of more Java-esque functions which return +the same values. These are of the format C<isI<Level>Enabled()>, +so C<$logger-E<gt>isDebugEnabled()> is synonymous to +C<$logger-E<gt>is_debug()>. + + +These level checking functions +will come in handy later, when we want to block unnecessary +expensive parameter construction in case the logging level is too +low to log the statement anyway, like in: + + if($logger->is_error()) { + $logger->error("Erroneous array: @super_long_array"); + } + +If we had just written + + $logger->error("Erroneous array: @super_long_array"); + +then Perl would have interpolated +C<@super_long_array> into the string via an expensive operation +only to figure out shortly after that the string can be ignored +entirely because the configured logging level is lower than C<$ERROR>. + +The to-be-logged +message passed to all of the functions described above can +consist of an arbitrary number of arguments, which the logging functions +just chain together to a single string. Therefore + + $logger->debug("Hello ", "World", "!"); # and + $logger->debug("Hello World!"); + +are identical. + +Note that even if one of the methods above returns true, it doesn't +necessarily mean that the message will actually get logged. +What is_debug() checks is that +the logger used is configured to let a message of the given priority +(DEBUG) through. But after this check, Log4perl will eventually apply custom +filters and forward the message to one or more appenders. None of this +gets checked by is_xxx(), for the simple reason that it's +impossible to know what a custom filter does with a message without +having the actual message or what an appender does to a message without +actually having it log it. + +=head2 Log and die or warn + +Often, when you croak / carp / warn / die, you want to log those messages. +Rather than doing the following: + + $logger->fatal($err) && die($err); + +you can use the following: + + $logger->logdie($err); + +And if instead of using + + warn($message); + $logger->warn($message); + +to both issue a warning via Perl's warn() mechanism and make sure you have +the same message in the log file as well, use: + + $logger->logwarn($message); + +Since there is +an ERROR level between WARN and FATAL, there are two additional helper +functions in case you'd like to use ERROR for either warn() or die(): + + $logger->error_warn(); + $logger->error_die(); + +Finally, there's the Carp functions that, in addition to logging, +also pass the stringified message to their companions in the Carp package: + + $logger->logcarp(); # warn w/ 1-level stack trace + $logger->logcluck(); # warn w/ full stack trace + $logger->logcroak(); # die w/ 1-level stack trace + $logger->logconfess(); # die w/ full stack trace + +=head2 Appenders + +If you don't define any appenders, nothing will happen. Appenders will +be triggered whenever the configured logging level requires a message +to be logged and not suppressed. + +C<Log::Log4perl> doesn't define any appenders by default, not even the root +logger has one. + +C<Log::Log4perl> already comes with a standard set of appenders: + + Log::Log4perl::Appender::Screen + Log::Log4perl::Appender::ScreenColoredLevels + Log::Log4perl::Appender::File + Log::Log4perl::Appender::Socket + Log::Log4perl::Appender::DBI + Log::Log4perl::Appender::Synchronized + Log::Log4perl::Appender::RRDs + +to log to the screen, to files and to databases. + +On CPAN, you can find additional appenders like + + Log::Log4perl::Layout::XMLLayout + +by Guido Carls E<lt>gcarls@cpan.orgE<gt>. +It allows for hooking up Log::Log4perl with the graphical Log Analyzer +Chainsaw (see +L<Log::Log4perl::FAQ/"Can I use Log::Log4perl with log4j's Chainsaw?">). + +=head2 Additional Appenders via Log::Dispatch + +C<Log::Log4perl> also supports I<Dave Rolskys> excellent C<Log::Dispatch> +framework which implements a wide variety of different appenders. + +Here's the list of appender modules currently available via C<Log::Dispatch>: + + Log::Dispatch::ApacheLog + Log::Dispatch::DBI (by Tatsuhiko Miyagawa) + Log::Dispatch::Email, + Log::Dispatch::Email::MailSend, + Log::Dispatch::Email::MailSendmail, + Log::Dispatch::Email::MIMELite + Log::Dispatch::File + Log::Dispatch::FileRotate (by Mark Pfeiffer) + Log::Dispatch::Handle + Log::Dispatch::Screen + Log::Dispatch::Syslog + Log::Dispatch::Tk (by Dominique Dumont) + +Please note that in order to use any of these additional appenders, you +have to fetch Log::Dispatch from CPAN and install it. Also the particular +appender you're using might require installing the particular module. + +For additional information on appenders, please check the +L<Log::Log4perl::Appender> manual page. + +=head2 Appender Example + +Now let's assume that we want to log C<info()> or +higher prioritized messages in the C<Foo::Bar> category +to both STDOUT and to a log file, say C<test.log>. +In the initialization section of your system, +just define two appenders using the readily available +C<Log::Log4perl::Appender::File> and C<Log::Log4perl::Appender::Screen> +modules: + + use Log::Log4perl; + + # Configuration in a string ... + my $conf = q( + log4perl.category.Foo.Bar = INFO, Logfile, Screen + + log4perl.appender.Logfile = Log::Log4perl::Appender::File + log4perl.appender.Logfile.filename = test.log + log4perl.appender.Logfile.layout = Log::Log4perl::Layout::PatternLayout + log4perl.appender.Logfile.layout.ConversionPattern = [%r] %F %L %m%n + + log4perl.appender.Screen = Log::Log4perl::Appender::Screen + log4perl.appender.Screen.stderr = 0 + log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout + ); + + # ... passed as a reference to init() + Log::Log4perl::init( \$conf ); + +Once the initialization shown above has happened once, typically in +the startup code of your system, just use the defined logger anywhere in +your system: + + ########################## + # ... in some function ... + ########################## + my $log = Log::Log4perl::get_logger("Foo::Bar"); + + # Logs both to STDOUT and to the file test.log + $log->info("Important Info!"); + +The C<layout> settings specified in the configuration section define the +format in which the +message is going to be logged by the specified appender. The format shown +for the file appender is logging not only the message but also the number of +milliseconds since the program has started (%r), the name of the file +the call to the logger has happened and the line number there (%F and +%L), the message itself (%m) and a OS-specific newline character (%n): + + [187] ./myscript.pl 27 Important Info! + +The +screen appender above, on the other hand, +uses a C<SimpleLayout>, which logs the +debug level, a hyphen (-) and the log message: + + INFO - Important Info! + +For more detailed info on layout formats, see L<Log Layouts>. + +In the configuration sample above, we chose to define a I<category> +logger (C<Foo::Bar>). +This will cause only messages originating from +this specific category logger to be logged in the defined format +and locations. + +=head2 Logging newlines + +There's some controversy between different logging systems as to when and +where newlines are supposed to be added to logged messages. + +The Log4perl way is that a logging statement I<should not> +contain a newline: + + $logger->info("Some message"); + $logger->info("Another message"); + +If this is supposed to end up in a log file like + + Some message + Another message + +then an appropriate appender layout like "%m%n" will take care of adding +a newline at the end of each message to make sure every message is +printed on its own line. + +Other logging systems, Log::Dispatch in particular, recommend adding the +newline to the log statement. This doesn't work well, however, if you, say, +replace your file appender by a database appender, and all of a sudden +those newlines scattered around the code don't make sense anymore. + +Assigning matching layouts to different appenders and leaving newlines +out of the code solves this problem. If you inherited code that has logging +statements with newlines and want to make it work with Log4perl, read +the L<Log::Log4perl::Layout::PatternLayout> documentation on how to +accomplish that. + +=head2 Configuration files + +As shown above, you can define C<Log::Log4perl> loggers both from within +your Perl code or from configuration files. The latter have the unbeatable +advantage that you can modify your system's logging behaviour without +interfering with the code at all. So even if your code is being run by +somebody who's totally oblivious to Perl, they still can adapt the +module's logging behaviour to their needs. + +C<Log::Log4perl> has been designed to understand C<Log4j> configuration +files -- as used by the original Java implementation. Instead of +reiterating the format description in [2], let me just list three +examples (also derived from [2]), which should also illustrate +how it works: + + log4j.rootLogger=DEBUG, A1 + log4j.appender.A1=org.apache.log4j.ConsoleAppender + log4j.appender.A1.layout=org.apache.log4j.PatternLayout + log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c %x - %m%n + +This enables messages of priority C<DEBUG> or higher in the root +hierarchy and has the system write them to the console. +C<ConsoleAppender> is a Java appender, but C<Log::Log4perl> jumps +through a significant number of hoops internally to map these to their +corresponding Perl classes, C<Log::Log4perl::Appender::Screen> in this case. + +Second example: + + log4perl.rootLogger=DEBUG, A1 + log4perl.appender.A1=Log::Log4perl::Appender::Screen + log4perl.appender.A1.layout=PatternLayout + log4perl.appender.A1.layout.ConversionPattern=%d %-5p %c - %m%n + log4perl.logger.com.foo=WARN + +This defines two loggers: The root logger and the C<com.foo> logger. +The root logger is easily triggered by debug-messages, +but the C<com.foo> logger makes sure that messages issued within +the C<Com::Foo> component and below are only forwarded to the appender +if they're of priority I<warning> or higher. + +Note that the C<com.foo> logger doesn't define an appender. Therefore, +it will just propagate the message up the hierarchy until the root logger +picks it up and forwards it to the one and only appender of the root +category, using the format defined for it. + +Third example: + + log4j.rootLogger=DEBUG, stdout, R + log4j.appender.stdout=org.apache.log4j.ConsoleAppender + log4j.appender.stdout.layout=org.apache.log4j.PatternLayout + log4j.appender.stdout.layout.ConversionPattern=%5p (%F:%L) - %m%n + log4j.appender.R=org.apache.log4j.RollingFileAppender + log4j.appender.R.File=example.log + log4j.appender.R.layout=org.apache.log4j.PatternLayout + log4j.appender.R.layout.ConversionPattern=%p %c - %m%n + +The root logger defines two appenders here: C<stdout>, which uses +C<org.apache.log4j.ConsoleAppender> (ultimately mapped by C<Log::Log4perl> +to L<Log::Log4perl::Appender::Screen>) to write to the screen. And +C<R>, a C<org.apache.log4j.RollingFileAppender> +(mapped by C<Log::Log4perl> to +L<Log::Dispatch::FileRotate> with the C<File> attribute specifying the +log file. + +See L<Log::Log4perl::Config> for more examples and syntax explanations. + +=head2 Log Layouts + +If the logging engine passes a message to an appender, because it thinks +it should be logged, the appender doesn't just +write it out haphazardly. There's ways to tell the appender how to format +the message and add all sorts of interesting data to it: The date and +time when the event happened, the file, the line number, the +debug level of the logger and others. + +There's currently two layouts defined in C<Log::Log4perl>: +C<Log::Log4perl::Layout::SimpleLayout> and +C<Log::Log4perl::Layout::PatternLayout>: + +=over 4 + +=item C<Log::Log4perl::SimpleLayout> + +formats a message in a simple +way and just prepends it by the debug level and a hyphen: +C<"$level - $message>, for example C<"FATAL - Can't open password file">. + +=item C<Log::Log4perl::Layout::PatternLayout> + +on the other hand is very powerful and +allows for a very flexible format in C<printf>-style. The format +string can contain a number of placeholders which will be +replaced by the logging engine when it's time to log the message: + + %c Category of the logging event. + %C Fully qualified package (or class) name of the caller + %d Current date in yyyy/MM/dd hh:mm:ss format + %F File where the logging event occurred + %H Hostname (if Sys::Hostname is available) + %l Fully qualified name of the calling method followed by the + callers source the file name and line number between + parentheses. + %L Line number within the file where the log statement was issued + %m The message to be logged + %m{chomp} The message to be logged, stripped off a trailing newline + %M Method or function where the logging request was issued + %n Newline (OS-independent) + %p Priority of the logging event + %P pid of the current process + %r Number of milliseconds elapsed from program start to logging + event + %R Number of milliseconds elapsed from last logging event to + current logging event + %T A stack trace of functions called + %x The topmost NDC (see below) + %X{key} The entry 'key' of the MDC (see below) + %% A literal percent (%) sign + +NDC and MDC are explained in L<"Nested Diagnostic Context (NDC)"> +and L<"Mapped Diagnostic Context (MDC)">. + +Also, C<%d> can be fine-tuned to display only certain characteristics +of a date, according to the SimpleDateFormat in the Java World +(L<http://java.sun.com/j2se/1.3/docs/api/java/text/SimpleDateFormat.html>) + +In this way, C<%d{HH:mm}> displays only hours and minutes of the current date, +while C<%d{yy, EEEE}> displays a two-digit year, followed by a spelled-out +(like C<Wednesday>). + +Similar options are available for shrinking the displayed category or +limit file/path components, C<%F{1}> only displays the source file I<name> +without any path components while C<%F> logs the full path. %c{2} only +logs the last two components of the current category, C<Foo::Bar::Baz> +becomes C<Bar::Baz> and saves space. + +If those placeholders aren't enough, then you can define your own right in +the config file like this: + + log4perl.PatternLayout.cspec.U = sub { return "UID $<" } + +See L<Log::Log4perl::Layout::PatternLayout> for further details on +customized specifiers. + +Please note that the subroutines you're defining in this way are going +to be run in the C<main> namespace, so be sure to fully qualify functions +and variables if they're located in different packages. + +SECURITY NOTE: this feature means arbitrary perl code can be embedded in the +config file. In the rare case where the people who have access to your config +file are different from the people who write your code and shouldn't have +execute rights, you might want to call + + Log::Log4perl::Config->allow_code(0); + +before you call init(). Alternatively you can supply a restricted set of +Perl opcodes that can be embedded in the config file as described in +L<"Restricting what Opcodes can be in a Perl Hook">. + +=back + +All placeholders are quantifiable, just like in I<printf>. Following this +tradition, C<%-20c> will reserve 20 chars for the category and left-justify it. + +For more details on logging and how to use the flexible and the simple +format, check out the original C<log4j> website under + +L<SimpleLayout|http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html> +and +L<PatternLayout|http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html> + +=head2 Penalties + +Logging comes with a price tag. C<Log::Log4perl> has been optimized +to allow for maximum performance, both with logging enabled and disabled. + +But you need to be aware that there's a small hit every time your code +encounters a log statement -- no matter if logging is enabled or not. +C<Log::Log4perl> has been designed to keep this so low that it will +be unnoticeable to most applications. + +Here's a couple of tricks which help C<Log::Log4perl> to avoid +unnecessary delays: + +You can save serious time if you're logging something like + + # Expensive in non-debug mode! + for (@super_long_array) { + $logger->debug("Element: $_"); + } + +and C<@super_long_array> is fairly big, so looping through it is pretty +expensive. Only you, the programmer, knows that going through that C<for> +loop can be skipped entirely if the current logging level for the +actual component is higher than C<debug>. +In this case, use this instead: + + # Cheap in non-debug mode! + if($logger->is_debug()) { + for (@super_long_array) { + $logger->debug("Element: $_"); + } + } + +If you're afraid that generating the parameters to the +logging function is fairly expensive, use closures: + + # Passed as subroutine ref + use Data::Dumper; + $logger->debug(sub { Dumper($data) } ); + +This won't unravel C<$data> via Dumper() unless it's actually needed +because it's logged. + +Also, Log::Log4perl lets you specify arguments +to logger functions in I<message output filter syntax>: + + $logger->debug("Structure: ", + { filter => \&Dumper, + value => $someref }); + +In this way, shortly before Log::Log4perl sending the +message out to any appenders, it will be searching all arguments for +hash references and treat them in a special way: + +It will invoke the function given as a reference with the C<filter> key +(C<Data::Dumper::Dumper()>) and pass it the value that came with +the key named C<value> as an argument. +The anonymous hash in the call above will be replaced by the return +value of the filter function. + +=head1 Categories + +B<Categories are also called "Loggers" in Log4perl, both refer +to the same thing and these terms are used interchangeably.> +C<Log::Log4perl> uses I<categories> to determine if a log statement in +a component should be executed or suppressed at the current logging level. +Most of the time, these categories are just the classes the log statements +are located in: + + package Candy::Twix; + + sub new { + my $logger = Log::Log4perl->get_logger("Candy::Twix"); + $logger->debug("Creating a new Twix bar"); + bless {}, shift; + } + + # ... + + package Candy::Snickers; + + sub new { + my $logger = Log::Log4perl->get_logger("Candy.Snickers"); + $logger->debug("Creating a new Snickers bar"); + bless {}, shift; + } + + # ... + + package main; + Log::Log4perl->init("mylogdefs.conf"); + + # => "LOG> Creating a new Snickers bar" + my $first = Candy::Snickers->new(); + # => "LOG> Creating a new Twix bar" + my $second = Candy::Twix->new(); + +Note that you can separate your category hierarchy levels +using either dots like +in Java (.) or double-colons (::) like in Perl. Both notations +are equivalent and are handled the same way internally. + +However, categories are just there to make +use of inheritance: if you invoke a logger in a sub-category, +it will bubble up the hierarchy and call the appropriate appenders. +Internally, categories are not related to the class hierarchy of the program +at all -- they're purely virtual. You can use arbitrary categories -- +for example in the following program, which isn't oo-style, but +procedural: + + sub print_portfolio { + + my $log = Log::Log4perl->get_logger("user.portfolio"); + $log->debug("Quotes requested: @_"); + + for(@_) { + print "$_: ", get_quote($_), "\n"; + } + } + + sub get_quote { + + my $log = Log::Log4perl->get_logger("internet.quotesystem"); + $log->debug("Fetching quote: $_[0]"); + + return yahoo_quote($_[0]); + } + +The logger in first function, C<print_portfolio>, is assigned the +(virtual) C<user.portfolio> category. Depending on the C<Log4perl> +configuration, this will either call a C<user.portfolio> appender, +a C<user> appender, or an appender assigned to root -- without +C<user.portfolio> having any relevance to the class system used in +the program. +The logger in the second function adheres to the +C<internet.quotesystem> category -- again, maybe because it's bundled +with other Internet functions, but not because there would be +a class of this name somewhere. + +However, be careful, don't go overboard: if you're developing a system +in object-oriented style, using the class hierarchy is usually your best +choice. Think about the people taking over your code one day: The +class hierarchy is probably what they know right up front, so it's easy +for them to tune the logging to their needs. + +=head2 Turn off a component + +C<Log4perl> doesn't only allow you to selectively switch I<on> a category +of log messages, you can also use the mechanism to selectively I<disable> +logging in certain components whereas logging is kept turned on in higher-level +categories. This mechanism comes in handy if you find that while bumping +up the logging level of a high-level (i. e. close to root) category, +that one component logs more than it should, + +Here's how it works: + + ############################################################ + # Turn off logging in a lower-level category while keeping + # it active in higher-level categories. + ############################################################ + log4perl.rootLogger=DEBUG, LOGFILE + log4perl.logger.deep.down.the.hierarchy = ERROR, LOGFILE + + # ... Define appenders ... + +This way, log messages issued from within +C<Deep::Down::The::Hierarchy> and below will be +logged only if they're C<ERROR> or worse, while in all other system components +even C<DEBUG> messages will be logged. + +=head2 Return Values + +All logging methods return values indicating if their message +actually reached one or more appenders. If the message has been +suppressed because of level constraints, C<undef> is returned. + +For example, + + my $ret = $logger->info("Message"); + +will return C<undef> if the system debug level for the current category +is not C<INFO> or more permissive. +If Log::Log4perl +forwarded the message to one or more appenders, the number of appenders +is returned. + +If appenders decide to veto on the message with an appender threshold, +the log method's return value will have them excluded. This means that if +you've got one appender holding an appender threshold and you're +logging a message +which passes the system's log level hurdle but not the appender threshold, +C<0> will be returned by the log function. + +The bottom line is: Logging functions will return a I<true> value if the message +made it through to one or more appenders and a I<false> value if it didn't. +This allows for constructs like + + $logger->fatal("@_") or print STDERR "@_\n"; + +which will ensure that the fatal message isn't lost +if the current level is lower than FATAL or printed twice if +the level is acceptable but an appender already points to STDERR. + +=head2 Pitfalls with Categories + +Be careful with just blindly reusing the system's packages as +categories. If you do, you'll get into trouble with inherited methods. +Imagine the following class setup: + + use Log::Log4perl; + + ########################################### + package Bar; + ########################################### + sub new { + my($class) = @_; + my $logger = Log::Log4perl::get_logger(__PACKAGE__); + $logger->debug("Creating instance"); + bless {}, $class; + } + ########################################### + package Bar::Twix; + ########################################### + our @ISA = qw(Bar); + + ########################################### + package main; + ########################################### + Log::Log4perl->init(\ qq{ + log4perl.category.Bar.Twix = DEBUG, Screen + log4perl.appender.Screen = Log::Log4perl::Appender::Screen + log4perl.appender.Screen.layout = SimpleLayout + }); + + my $bar = Bar::Twix->new(); + +C<Bar::Twix> just inherits everything from C<Bar>, including the constructor +C<new()>. +Contrary to what you might be thinking at first, this won't log anything. +Reason for this is the C<get_logger()> call in package C<Bar>, which +will always get a logger of the C<Bar> category, even if we call C<new()> via +the C<Bar::Twix> package, which will make perl go up the inheritance +tree to actually execute C<Bar::new()>. Since we've only defined logging +behaviour for C<Bar::Twix> in the configuration file, nothing will happen. + +This can be fixed by changing the C<get_logger()> method in C<Bar::new()> +to obtain a logger of the category matching the +I<actual> class of the object, like in + + # ... in Bar::new() ... + my $logger = Log::Log4perl::get_logger( $class ); + +In a method other than the constructor, the class name of the actual +object can be obtained by calling C<ref()> on the object reference, so + + package BaseClass; + use Log::Log4perl qw( get_logger ); + + sub new { + bless {}, shift; + } + + sub method { + my( $self ) = @_; + + get_logger( ref $self )->debug( "message" ); + } + + package SubClass; + our @ISA = qw(BaseClass); + +is the recommended pattern to make sure that + + my $sub = SubClass->new(); + $sub->meth(); + +starts logging if the C<"SubClass"> category +(and not the C<"BaseClass"> category has logging enabled at the DEBUG level. + +=head2 Initialize once and only once + +It's important to realize that Log::Log4perl gets initialized once and only +once, typically at the start of a program or system. Calling C<init()> +more than once will cause it to clobber the existing configuration and +I<replace> it by the new one. + +If you're in a traditional CGI environment, where every request is +handled by a new process, calling C<init()> every time is fine. In +persistent environments like C<mod_perl>, however, Log::Log4perl +should be initialized either at system startup time (Apache offers +startup handlers for that) or via + + # Init or skip if already done + Log::Log4perl->init_once($conf_file); + +C<init_once()> is identical to C<init()>, just with the exception +that it will leave a potentially existing configuration alone and +will only call C<init()> if Log::Log4perl hasn't been initialized yet. + +If you're just curious if Log::Log4perl has been initialized yet, the +check + + if(Log::Log4perl->initialized()) { + # Yes, Log::Log4perl has already been initialized + } else { + # No, not initialized yet ... + } + +can be used. + +If you're afraid that the components of your system are stepping on +each other's toes or if you are thinking that different components should +initialize Log::Log4perl separately, try to consolidate your system +to use a centralized Log4perl configuration file and use +Log4perl's I<categories> to separate your components. + +=head2 Custom Filters + +Log4perl allows the use of customized filters in its appenders +to control the output of messages. These filters might grep for +certain text chunks in a message, verify that its priority +matches or exceeds a certain level or that this is the 10th +time the same message has been submitted -- and come to a log/no log +decision based upon these circumstantial facts. + +Check out L<Log::Log4perl::Filter> for detailed instructions +on how to use them. + +=head2 Performance + +The performance of Log::Log4perl calls obviously depends on a lot of things. +But to give you a general idea, here's some rough numbers: + +On a Pentium 4 Linux box at 2.4 GHz, you'll get through + +=over 4 + +=item * + +500,000 suppressed log statements per second + +=item * + +30,000 logged messages per second (using an in-memory appender) + +=item * + +init_and_watch delay mode: 300,000 suppressed, 30,000 logged. +init_and_watch signal mode: 450,000 suppressed, 30,000 logged. + +=back + +Numbers depend on the complexity of the Log::Log4perl configuration. +For a more detailed benchmark test, check the C<docs/benchmark.results.txt> +document in the Log::Log4perl distribution. + +=head1 Cool Tricks + +Here's a collection of useful tricks for the advanced C<Log::Log4perl> user. +For more, check the FAQ, either in the distribution +(L<Log::Log4perl::FAQ>) or on L<http://log4perl.sourceforge.net>. + +=head2 Shortcuts + +When getting an instance of a logger, instead of saying + + use Log::Log4perl; + my $logger = Log::Log4perl->get_logger(); + +it's often more convenient to import the C<get_logger> method from +C<Log::Log4perl> into the current namespace: + + use Log::Log4perl qw(get_logger); + my $logger = get_logger(); + +Please note this difference: To obtain the root logger, please use +C<get_logger("")>, call it without parameters (C<get_logger()>), you'll +get the logger of a category named after the current package. +C<get_logger()> is equivalent to C<get_logger(__PACKAGE__)>. + +=head2 Alternative initialization + +Instead of having C<init()> read in a configuration file by specifying +a file name or passing it a reference to an open filehandle +(C<Log::Log4perl-E<gt>init( \*FILE )>), +you can +also pass in a reference to a string, containing the content of +the file: + + Log::Log4perl->init( \$config_text ); + +Also, if you've got the C<name=value> pairs of the configuration in +a hash, you can just as well initialize C<Log::Log4perl> with +a reference to it: + + my %key_value_pairs = ( + "log4perl.rootLogger" => "ERROR, LOGFILE", + "log4perl.appender.LOGFILE" => "Log::Log4perl::Appender::File", + ... + ); + + Log::Log4perl->init( \%key_value_pairs ); + +Or also you can use a URL, see below: + +=head2 Using LWP to parse URLs + +(This section borrowed from XML::DOM::Parser by T.J. Mather). + +The init() function now also supports URLs, e.g. I<http://www.erols.com/enno/xsa.xml>. +It uses LWP to download the file and then calls parse() on the resulting string. +By default it will use a L<LWP::UserAgent> that is created as follows: + + use LWP::UserAgent; + $LWP_USER_AGENT = LWP::UserAgent->new; + $LWP_USER_AGENT->env_proxy; + +Note that env_proxy reads proxy settings from environment variables, which is what I need to +do to get thru our firewall. If you want to use a different LWP::UserAgent, you can +set it with + + Log::Log4perl::Config::set_LWP_UserAgent($my_agent); + +Currently, LWP is used when the filename (passed to parsefile) starts with one of +the following URL schemes: http, https, ftp, wais, gopher, or file (followed by a colon.) + +Don't use this feature with init_and_watch(). + +=head2 Automatic reloading of changed configuration files + +Instead of just statically initializing Log::Log4perl via + + Log::Log4perl->init($conf_file); + +there's a way to have Log::Log4perl periodically check for changes +in the configuration and reload it if necessary: + + Log::Log4perl->init_and_watch($conf_file, $delay); + +In this mode, Log::Log4perl will examine the configuration file +C<$conf_file> every C<$delay> seconds for changes via the file's +last modification timestamp. If the file has been updated, it will +be reloaded and replace the current Log::Log4perl configuration. + +The way this works is that with every logger function called +(debug(), is_debug(), etc.), Log::Log4perl will check if the delay +interval has expired. If so, it will run a -M file check on the +configuration file. If its timestamp has been modified, the current +configuration will be dumped and new content of the file will be +loaded. + +This convenience comes at a price, though: Calling time() with every +logging function call, especially the ones that are "suppressed" (!), +will slow down these Log4perl calls by about 40%. + +To alleviate this performance hit a bit, C<init_and_watch()> +can be configured to listen for a Unix signal to reload the +configuration instead: + + Log::Log4perl->init_and_watch($conf_file, 'HUP'); + +This will set up a signal handler for SIGHUP and reload the configuration +if the application receives this signal, e.g. via the C<kill> command: + + kill -HUP pid + +where C<pid> is the process ID of the application. This will bring you back +to about 85% of Log::Log4perl's normal execution speed for suppressed +statements. For details, check out L<"Performance">. For more info +on the signal handler, look for L<Log::Log4perl::Config::Watch/"SIGNAL MODE">. + +If you have a somewhat long delay set between physical config file checks +or don't want to use the signal associated with the config file watcher, +you can trigger a configuration reload at the next possible time by +calling C<Log::Log4perl::Config-E<gt>watcher-E<gt>force_next_check()>. + +One thing to watch out for: If the configuration file contains a syntax +or other fatal error, a running application will stop with C<die> if +this damaged configuration will be loaded during runtime, triggered +either by a signal or if the delay period expired and the change is +detected. This behaviour might change in the future. + +To allow the application to intercept and control a configuration reload +in init_and_watch mode, a callback can be specified: + + Log::Log4perl->init_and_watch($conf_file, 10, { + preinit_callback => \&callback }); + +If Log4perl determines that the configuration needs to be reloaded, it will +call the C<preinit_callback> function without parameters. If the callback +returns a true value, Log4perl will proceed and reload the configuration. If +the callback returns a false value, Log4perl will keep the old configuration +and skip reloading it until the next time around. Inside the callback, an +application can run all kinds of checks, including accessing the configuration +file, which is available via +C<Log::Log4perl::Config-E<gt>watcher()-E<gt>file()>. + +=head2 Variable Substitution + +To avoid having to retype the same expressions over and over again, +Log::Log4perl's configuration files support simple variable substitution. +New variables are defined simply by adding + + varname = value + +lines to the configuration file before using + + ${varname} + +afterwards to recall the assigned values. Here's an example: + + layout_class = Log::Log4perl::Layout::PatternLayout + layout_pattern = %d %F{1} %L> %m %n + + log4perl.category.Bar.Twix = WARN, Logfile, Screen + + log4perl.appender.Logfile = Log::Log4perl::Appender::File + log4perl.appender.Logfile.filename = test.log + log4perl.appender.Logfile.layout = ${layout_class} + log4perl.appender.Logfile.layout.ConversionPattern = ${layout_pattern} + + log4perl.appender.Screen = Log::Log4perl::Appender::Screen + log4perl.appender.Screen.layout = ${layout_class} + log4perl.appender.Screen.layout.ConversionPattern = ${layout_pattern} + +This is a convenient way to define two appenders with the same layout +without having to retype the pattern definitions. + +Variable substitution via C<${varname}> +will first try to find an explicitly defined +variable. If that fails, it will check your shell's environment +for a variable of that name. If that also fails, the program will C<die()>. + +=head2 Perl Hooks in the Configuration File + +If some of the values used in the Log4perl configuration file +need to be dynamically modified by the program, use Perl hooks: + + log4perl.appender.File.filename = \ + sub { return getLogfileName(); } + +Each value starting with the string C<sub {...> is interpreted as Perl code to +be executed at the time the application parses the configuration +via C<Log::Log4perl::init()>. The return value of the subroutine +is used by Log::Log4perl as the configuration value. + +The Perl code is executed in the C<main> package, functions in +other packages have to be called in fully-qualified notation. + +Here's another example, utilizing an environment variable as a +username for a DBI appender: + + log4perl.appender.DB.username = \ + sub { $ENV{DB_USER_NAME } } + +However, please note the difference between these code snippets and those +used for user-defined conversion specifiers as discussed in +L<Log::Log4perl::Layout::PatternLayout>: +While the snippets above are run I<once> +when C<Log::Log4perl::init()> is called, the conversion specifier +snippets are executed I<each time> a message is rendered according to +the PatternLayout. + +SECURITY NOTE: this feature means arbitrary perl code can be embedded in the +config file. In the rare case where the people who have access to your config +file are different from the people who write your code and shouldn't have +execute rights, you might want to set + + Log::Log4perl::Config->allow_code(0); + +before you call init(). Alternatively you can supply a restricted set of +Perl opcodes that can be embedded in the config file as described in +L<"Restricting what Opcodes can be in a Perl Hook">. + +=head2 Restricting what Opcodes can be in a Perl Hook + +The value you pass to Log::Log4perl::Config->allow_code() determines whether +the code that is embedded in the config file is eval'd unrestricted, or +eval'd in a Safe compartment. By default, a value of '1' is assumed, +which does a normal 'eval' without any restrictions. A value of '0' +however prevents any embedded code from being evaluated. + +If you would like fine-grained control over what can and cannot be included +in embedded code, then please utilize the following methods: + + Log::Log4perl::Config->allow_code( $allow ); + Log::Log4perl::Config->allowed_code_ops($op1, $op2, ... ); + Log::Log4perl::Config->vars_shared_with_safe_compartment( [ \%vars | $package, \@vars ] ); + Log::Log4perl::Config->allowed_code_ops_convenience_map( [ \%map | $name, \@mask ] ); + +Log::Log4perl::Config-E<gt>allowed_code_ops() takes a list of opcode masks +that are allowed to run in the compartment. The opcode masks must be +specified as described in L<Opcode>: + + Log::Log4perl::Config->allowed_code_ops(':subprocess'); + +This example would allow Perl operations like backticks, system, fork, and +waitpid to be executed in the compartment. Of course, you probably don't +want to use this mask -- it would allow exactly what the Safe compartment is +designed to prevent. + +Log::Log4perl::Config-E<gt>vars_shared_with_safe_compartment() +takes the symbols which +should be exported into the Safe compartment before the code is evaluated. +The keys of this hash are the package names that the symbols are in, and the +values are array references to the literal symbol names. For convenience, +the default settings export the '%ENV' hash from the 'main' package into the +compartment: + + Log::Log4perl::Config->vars_shared_with_safe_compartment( + main => [ '%ENV' ], + ); + +Log::Log4perl::Config-E<gt>allowed_code_ops_convenience_map() is an accessor +method to a map of convenience names to opcode masks. At present, the +following convenience names are defined: + + safe = [ ':browse' ] + restrictive = [ ':default' ] + +For convenience, if Log::Log4perl::Config-E<gt>allow_code() is called with a +value which is a key of the map previously defined with +Log::Log4perl::Config-E<gt>allowed_code_ops_convenience_map(), then the +allowed opcodes are set according to the value defined in the map. If this +is confusing, consider the following: + + use Log::Log4perl; + + my $config = <<'END'; + log4perl.logger = INFO, Main + log4perl.appender.Main = Log::Log4perl::Appender::File + log4perl.appender.Main.filename = \ + sub { "example" . getpwuid($<) . ".log" } + log4perl.appender.Main.layout = Log::Log4perl::Layout::SimpleLayout + END + + $Log::Log4perl::Config->allow_code('restrictive'); + Log::Log4perl->init( \$config ); # will fail + $Log::Log4perl::Config->allow_code('safe'); + Log::Log4perl->init( \$config ); # will succeed + +The reason that the first call to -E<gt>init() fails is because the +'restrictive' name maps to an opcode mask of ':default'. getpwuid() is not +part of ':default', so -E<gt>init() fails. The 'safe' name maps to an opcode +mask of ':browse', which allows getpwuid() to run, so -E<gt>init() succeeds. + +allowed_code_ops_convenience_map() can be invoked in several ways: + +=over 4 + +=item allowed_code_ops_convenience_map() + +Returns the entire convenience name map as a hash reference in scalar +context or a hash in list context. + +=item allowed_code_ops_convenience_map( \%map ) + +Replaces the entire convenience name map with the supplied hash reference. + +=item allowed_code_ops_convenience_map( $name ) + +Returns the opcode mask for the given convenience name, or undef if no such +name is defined in the map. + +=item allowed_code_ops_convenience_map( $name, \@mask ) + +Adds the given name/mask pair to the convenience name map. If the name +already exists in the map, it's value is replaced with the new mask. + +=back + +as can vars_shared_with_safe_compartment(): + +=over 4 + +=item vars_shared_with_safe_compartment() + +Return the entire map of packages to variables as a hash reference in scalar +context or a hash in list context. + +=item vars_shared_with_safe_compartment( \%packages ) + +Replaces the entire map of packages to variables with the supplied hash +reference. + +=item vars_shared_with_safe_compartment( $package ) + +Returns the arrayref of variables to be shared for a specific package. + +=item vars_shared_with_safe_compartment( $package, \@vars ) + +Adds the given package / varlist pair to the map. If the package already +exists in the map, it's value is replaced with the new arrayref of variable +names. + +=back + +For more information on opcodes and Safe Compartments, see L<Opcode> and +L<Safe>. + +=head2 Changing the Log Level on a Logger + +Log4perl provides some internal functions for quickly adjusting the +log level from within a running Perl program. + +Now, some people might +argue that you should adjust your levels from within an external +Log4perl configuration file, but Log4perl is everybody's darling. + +Typically run-time adjusting of levels is done +at the beginning, or in response to some external input (like a +"more logging" runtime command for diagnostics). + +You get the log level from a logger object with: + + $current_level = $logger->level(); + +and you may set it with the same method, provided you first +imported the log level constants, with: + + use Log::Log4perl::Level; + +Then you can set the level on a logger to one of the constants, + + $logger->level($ERROR); # one of DEBUG, INFO, WARN, ERROR, FATAL + +To B<increase> the level of logging currently being done, use: + + $logger->more_logging($delta); + +and to B<decrease> it, use: + + $logger->less_logging($delta); + +$delta must be a positive integer (for now, we may fix this later ;). + +There are also two equivalent functions: + + $logger->inc_level($delta); + $logger->dec_level($delta); + +They're included to allow you a choice in readability. Some folks +will prefer more/less_logging, as they're fairly clear in what they +do, and allow the programmer not to worry too much about what a Level +is and whether a higher Level means more or less logging. However, +other folks who do understand and have lots of code that deals with +levels will probably prefer the inc_level() and dec_level() methods as +they want to work with Levels and not worry about whether that means +more or less logging. :) + +That diatribe aside, typically you'll use more_logging() or inc_level() +as such: + + my $v = 0; # default level of verbosity. + + GetOptions("v+" => \$v, ...); + + if( $v ) { + $logger->more_logging($v); # inc logging level once for each -v in ARGV + } + +=head2 Custom Log Levels + +First off, let me tell you that creating custom levels is heavily +deprecated by the log4j folks. Indeed, instead of creating additional +levels on top of the predefined DEBUG, INFO, WARN, ERROR and FATAL, +you should use categories to control the amount of logging smartly, +based on the location of the log-active code in the system. + +Nevertheless, +Log4perl provides a nice way to create custom levels via the +create_custom_level() routine function. However, this must be done +before the first call to init() or get_logger(). Say you want to create +a NOTIFY logging level that comes after WARN (and thus before INFO). +You'd do such as follows: + + use Log::Log4perl; + use Log::Log4perl::Level; + + Log::Log4perl::Logger::create_custom_level("NOTIFY", "WARN"); + +And that's it! create_custom_level() creates the following functions / +variables for level FOO: + + $FOO_INT # integer to use in L4p::Level::to_level() + $logger->foo() # log function to log if level = FOO + $logger->is_foo() # true if current level is >= FOO + +These levels can also be used in your +config file, but note that your config file probably won't be +portable to another log4perl or log4j environment unless you've +made the appropriate mods there too. + +Since Log4perl translates log levels to syslog and Log::Dispatch if +their appenders are used, you may add mappings for custom levels as well: + + Log::Log4perl::Level::add_priority("NOTIFY", "WARN", + $syslog_equiv, $log_dispatch_level); + +For example, if your new custom "NOTIFY" level is supposed to map +to syslog level 2 ("LOG_NOTICE") and Log::Dispatch level 2 ("notice"), use: + + Log::Log4perl::Logger::create_custom_level("NOTIFY", "WARN", 2, 2); + +=head2 System-wide log levels + +As a fairly drastic measure to decrease (or increase) the logging level +all over the system with one single configuration option, use the C<threshold> +keyword in the Log4perl configuration file: + + log4perl.threshold = ERROR + +sets the system-wide (or hierarchy-wide according to the log4j documentation) +to ERROR and therefore deprives every logger in the system of the right +to log lower-prio messages. + +=head2 Easy Mode + +For teaching purposes (especially for [1]), I've put C<:easy> mode into +C<Log::Log4perl>, which just initializes a single root logger with a +defined priority and a screen appender including some nice standard layout: + + ### Initialization Section + use Log::Log4perl qw(:easy); + Log::Log4perl->easy_init($ERROR); # Set priority of root logger to ERROR + + ### Application Section + my $logger = get_logger(); + $logger->fatal("This will get logged."); + $logger->debug("This won't."); + +This will dump something like + + 2002/08/04 11:43:09 ERROR> script.pl:16 main::function - This will get logged. + +to the screen. While this has been proven to work well familiarizing people +with C<Log::Logperl> slowly, effectively avoiding to clobber them over the +head with a +plethora of different knobs to fiddle with (categories, appenders, levels, +layout), the overall mission of C<Log::Log4perl> is to let people use +categories right from the start to get used to the concept. So, let's keep +this one fairly hidden in the man page (congrats on reading this far :). + +=head2 Stealth loggers + +Sometimes, people are lazy. If you're whipping up a 50-line script and want +the comfort of Log::Log4perl without having the burden of carrying a +separate log4perl.conf file or a 5-liner defining that you want to append +your log statements to a file, you can use the following features: + + use Log::Log4perl qw(:easy); + + Log::Log4perl->easy_init( { level => $DEBUG, + file => ">>test.log" } ); + + # Logs to test.log via stealth logger + DEBUG("Debug this!"); + INFO("Info this!"); + WARN("Warn this!"); + ERROR("Error this!"); + + some_function(); + + sub some_function { + # Same here + FATAL("Fatal this!"); + } + +In C<:easy> mode, C<Log::Log4perl> will instantiate a I<stealth logger> +and introduce the +convenience functions C<TRACE>, C<DEBUG()>, C<INFO()>, C<WARN()>, +C<ERROR()>, C<FATAL()>, and C<ALWAYS> into the package namespace. +These functions simply take messages as +arguments and forward them to the stealth loggers methods (C<debug()>, +C<info()>, and so on). + +If a message should never be blocked, regardless of the log level, +use the C<ALWAYS> function which corresponds to a log level of C<OFF>: + + ALWAYS "This will be printed regardless of the log level"; + +The C<easy_init> method can be called with a single level value to +create a STDERR appender and a root logger as in + + Log::Log4perl->easy_init($DEBUG); + +or, as shown below (and in the example above) +with a reference to a hash, specifying values +for C<level> (the logger's priority), C<file> (the appender's data sink), +C<category> (the logger's category and C<layout> for the appender's +pattern layout specification. +All key-value pairs are optional, they +default to C<$DEBUG> for C<level>, C<STDERR> for C<file>, +C<""> (root category) for C<category> and +C<%d %m%n> for C<layout>: + + Log::Log4perl->easy_init( { level => $DEBUG, + file => ">test.log", + utf8 => 1, + category => "Bar::Twix", + layout => '%F{1}-%L-%M: %m%n' } ); + +The C<file> parameter takes file names preceded by C<"E<gt>"> +(overwrite) and C<"E<gt>E<gt>"> (append) as arguments. This will +cause C<Log::Log4perl::Appender::File> appenders to be created behind +the scenes. Also the keywords C<STDOUT> and C<STDERR> (no C<E<gt>> or +C<E<gt>E<gt>>) are recognized, which will utilize and configure +C<Log::Log4perl::Appender::Screen> appropriately. The C<utf8> flag, +if set to a true value, runs a C<binmode> command on the file handle +to establish a utf8 line discipline on the file, otherwise you'll get a +'wide character in print' warning message and probably not what you'd +expect as output. + +The stealth loggers can be used in different packages, you just need to make +sure you're calling the "use" function in every package you're using +C<Log::Log4perl>'s easy services: + + package Bar::Twix; + use Log::Log4perl qw(:easy); + sub eat { DEBUG("Twix mjam"); } + + package Bar::Mars; + use Log::Log4perl qw(:easy); + sub eat { INFO("Mars mjam"); } + + package main; + + use Log::Log4perl qw(:easy); + + Log::Log4perl->easy_init( { level => $DEBUG, + file => ">>test.log", + category => "Bar::Twix", + layout => '%F{1}-%L-%M: %m%n' }, + { level => $DEBUG, + file => "STDOUT", + category => "Bar::Mars", + layout => '%m%n' }, + ); + Bar::Twix::eat(); + Bar::Mars::eat(); + +As shown above, C<easy_init()> will take any number of different logger +definitions as hash references. + +Also, stealth loggers feature the functions C<LOGWARN()>, C<LOGDIE()>, +and C<LOGEXIT()>, +combining a logging request with a subsequent Perl warn() or die() or exit() +statement. So, for example + + if($all_is_lost) { + LOGDIE("Terrible Problem"); + } + +will log the message if the package's logger is at least C<FATAL> but +C<die()> (including the traditional output to STDERR) in any case afterwards. + +See L<"Log and die or warn"> for the similar C<logdie()> and C<logwarn()> +functions of regular (i.e non-stealth) loggers. + +Similarily, C<LOGCARP()>, C<LOGCLUCK()>, C<LOGCROAK()>, and C<LOGCONFESS()> +are provided in C<:easy> mode, facilitating the use of C<logcarp()>, +C<logcluck()>, C<logcroak()>, and C<logconfess()> with stealth loggers. + +B<When using Log::Log4perl in easy mode, +please make sure you understand the implications of +L</"Pitfalls with Categories">>. + +By the way, these convenience functions perform exactly as fast as the +standard Log::Log4perl logger methods, there's I<no> performance penalty +whatsoever. + +=head2 Nested Diagnostic Context (NDC) + +If you find that your application could use a global (thread-specific) +data stack which your loggers throughout the system have easy access to, +use Nested Diagnostic Contexts (NDCs). Also check out +L<"Mapped Diagnostic Context (MDC)">, this might turn out to be even more +useful. + +For example, when handling a request of a web client, it's probably +useful to have the user's IP address available in all log statements +within code dealing with this particular request. Instead of passing +this piece of data around between your application functions, you can just +use the global (but thread-specific) NDC mechanism. It allows you +to push data pieces (scalars usually) onto its stack via + + Log::Log4perl::NDC->push("San"); + Log::Log4perl::NDC->push("Francisco"); + +and have your loggers retrieve them again via the "%x" placeholder in +the PatternLayout. With the stack values above and a PatternLayout format +like "%x %m%n", the call + + $logger->debug("rocks"); + +will end up as + + San Francisco rocks + +in the log appender. + +The stack mechanism allows for nested structures. +Just make sure that at the end of the request, you either decrease the stack +one by one by calling + + Log::Log4perl::NDC->pop(); + Log::Log4perl::NDC->pop(); + +or clear out the entire NDC stack by calling + + Log::Log4perl::NDC->remove(); + +Even if you should forget to do that, C<Log::Log4perl> won't grow the stack +indefinitely, but limit it to a maximum, defined in C<Log::Log4perl::NDC> +(currently 5). A call to C<push()> on a full stack will just replace +the topmost element by the new value. + +Again, the stack is always available via the "%x" placeholder +in the Log::Log4perl::Layout::PatternLayout class whenever a logger +fires. It will replace "%x" by the blank-separated list of the +values on the stack. It does that by just calling + + Log::Log4perl::NDC->get(); + +internally. See details on how this standard log4j feature is implemented +in L<Log::Log4perl::NDC>. + +=head2 Mapped Diagnostic Context (MDC) + +Just like the previously discussed NDC stores thread-specific +information in a stack structure, the MDC implements a hash table +to store key/value pairs in. + +The static method + + Log::Log4perl::MDC->put($key, $value); + +stores C<$value> under a key C<$key>, with which it can be retrieved later +(possibly in a totally different part of the system) by calling +the C<get> method: + + my $value = Log::Log4perl::MDC->get($key); + +If no value has been stored previously under C<$key>, the C<get> method +will return C<undef>. + +Typically, MDC values are retrieved later on via the C<"%X{...}"> placeholder +in C<Log::Log4perl::Layout::PatternLayout>. If the C<get()> method +returns C<undef>, the placeholder will expand to the string C<[undef]>. + +An application taking a web request might store the remote host +like + + Log::Log4perl::MDC->put("remote_host", $r->headers("HOST")); + +at its beginning and if the appender's layout looks something like + + log4perl.appender.Logfile.layout.ConversionPattern = %X{remote_host}: %m%n + +then a log statement like + + DEBUG("Content delivered"); + +will log something like + + adsl-63.dsl.snf.pacbell.net: Content delivered + +later on in the program. + +For details, please check L<Log::Log4perl::MDC>. + +=head2 Resurrecting hidden Log4perl Statements + +Sometimes scripts need to be deployed in environments without having +Log::Log4perl installed yet. On the other hand, you don't want to +live without your Log4perl statements -- they're gonna come in +handy later. + +So, just deploy your script with Log4perl statements commented out with the +pattern C<###l4p>, like in + + ###l4p DEBUG "It works!"; + # ... + ###l4p INFO "Really!"; + +If Log::Log4perl is available, +use the C<:resurrect> tag to have Log4perl resurrect those buried +statements before the script starts running: + + use Log::Log4perl qw(:resurrect :easy); + + ###l4p Log::Log4perl->easy_init($DEBUG); + ###l4p DEBUG "It works!"; + # ... + ###l4p INFO "Really!"; + +This will have a source filter kick in and indeed print + + 2004/11/18 22:08:46 It works! + 2004/11/18 22:08:46 Really! + +In environments lacking Log::Log4perl, just comment out the first line +and the script will run nevertheless (but of course without logging): + + # use Log::Log4perl qw(:resurrect :easy); + + ###l4p Log::Log4perl->easy_init($DEBUG); + ###l4p DEBUG "It works!"; + # ... + ###l4p INFO "Really!"; + +because everything's a regular comment now. Alternatively, put the +magic Log::Log4perl comment resurrection line into your shell's +PERL5OPT environment variable, e.g. for bash: + + set PERL5OPT=-MLog::Log4perl=:resurrect,:easy + export PERL5OPT + +This will awaken the giant within an otherwise silent script like +the following: + + #!/usr/bin/perl + + ###l4p Log::Log4perl->easy_init($DEBUG); + ###l4p DEBUG "It works!"; + +As of C<Log::Log4perl> 1.12, you can even force I<all> modules +loaded by a script to have their hidden Log4perl statements +resurrected. For this to happen, load C<Log::Log4perl::Resurrector> +I<before> loading any modules: + + use Log::Log4perl qw(:easy); + use Log::Log4perl::Resurrector; + + use Foobar; # All hidden Log4perl statements in here will + # be uncommented before Foobar gets loaded. + + Log::Log4perl->easy_init($DEBUG); + ... + +Check the C<Log::Log4perl::Resurrector> manpage for more details. + +=head2 Access defined appenders + +All appenders defined in the configuration file or via Perl code +can be retrieved by the C<appender_by_name()> class method. This comes +in handy if you want to manipulate or query appender properties after +the Log4perl configuration has been loaded via C<init()>. + +Note that internally, Log::Log4perl uses the C<Log::Log4perl::Appender> +wrapper class to control the real appenders (like +C<Log::Log4perl::Appender::File> or C<Log::Dispatch::FileRotate>). +The C<Log::Log4perl::Appender> class has an C<appender> attribute, +pointing to the real appender. + +The reason for this is that external appenders like +C<Log::Dispatch::FileRotate> don't support all of Log::Log4perl's +appender control mechanisms (like appender thresholds). + +The previously mentioned method C<appender_by_name()> returns a +reference to the I<real> appender object. If you want access to the +wrapper class (e.g. if you want to modify the appender's threshold), +use the hash C<$Log::Log4perl::Logger::APPENDER_BY_NAME{...}> instead, +which holds references to all appender wrapper objects. + +=head2 Modify appender thresholds + +To set an appender's threshold, use its C<threshold()> method: + + $app->threshold( $FATAL ); + +To conveniently adjust I<all> appender thresholds (e.g. because a script +uses more_logging()), use + + # decrease thresholds of all appenders + Log::Log4perl->appender_thresholds_adjust(-1); + +This will decrease the thresholds of all appenders in the system by +one level, i.e. WARN becomes INFO, INFO becomes DEBUG, etc. To only modify +selected ones, use + + # decrease thresholds of all appenders + Log::Log4perl->appender_thresholds_adjust(-1, ['AppName1', ...]); + +and pass the names of affected appenders in a ref to an array. + +=head1 Advanced configuration within Perl + +Initializing Log::Log4perl can certainly also be done from within Perl. +At last, this is what C<Log::Log4perl::Config> does behind the scenes. +Log::Log4perl's configuration file parsers are using a publically +available API to set up Log::Log4perl's categories, appenders and layouts. + +Here's an example on how to configure two appenders with the same layout +in Perl, without using a configuration file at all: + + ######################## + # Initialization section + ######################## + use Log::Log4perl; + use Log::Log4perl::Layout; + use Log::Log4perl::Level; + + # Define a category logger + my $log = Log::Log4perl->get_logger("Foo::Bar"); + + # Define a layout + my $layout = Log::Log4perl::Layout::PatternLayout->new("[%r] %F %L %m%n"); + + # Define a file appender + my $file_appender = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::File", + name => "filelog", + filename => "/tmp/my.log"); + + # Define a stdout appender + my $stdout_appender = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::Screen", + name => "screenlog", + stderr => 0); + + # Have both appenders use the same layout (could be different) + $stdout_appender->layout($layout); + $file_appender->layout($layout); + + $log->add_appender($stdout_appender); + $log->add_appender($file_appender); + $log->level($INFO); + +Please note the class of the appender object is passed as a I<string> to +C<Log::Log4perl::Appender> in the I<first> argument. Behind the scenes, +C<Log::Log4perl::Appender> will create the necessary +C<Log::Log4perl::Appender::*> (or C<Log::Dispatch::*>) object and pass +along the name value pairs we provided to +C<Log::Log4perl::Appender-E<gt>new()> after the first argument. + +The C<name> value is optional and if you don't provide one, +C<Log::Log4perl::Appender-E<gt>new()> will create a unique one for you. +The names and values of additional parameters are dependent on the requirements +of the particular appender class and can be looked up in their +manual pages. + +A side note: In case you're wondering if +C<Log::Log4perl::Appender-E<gt>new()> will also take care of the +C<min_level> argument to the C<Log::Dispatch::*> constructors called +behind the scenes -- yes, it does. This is because we want the +C<Log::Dispatch> objects to blindly log everything we send them +(C<debug> is their lowest setting) because I<we> in C<Log::Log4perl> +want to call the shots and decide on when and what to log. + +The call to the appender's I<layout()> method specifies the format (as a +previously created C<Log::Log4perl::Layout::PatternLayout> object) in which the +message is being logged in the specified appender. +If you don't specify a layout, the logger will fall back to +C<Log::Log4perl::SimpleLayout>, which logs the debug level, a hyphen (-) +and the log message. + +Layouts are objects, here's how you create them: + + # Create a simple layout + my $simple = Log::Log4perl::SimpleLayout(); + + # create a flexible layout: + # ("yyyy/MM/dd hh:mm:ss (file:lineno)> message\n") + my $pattern = Log::Log4perl::Layout::PatternLayout("%d (%F:%L)> %m%n"); + +Every appender has exactly one layout assigned to it. You assign +the layout to the appender using the appender's C<layout()> object: + + my $app = Log::Log4perl::Appender->new( + "Log::Log4perl::Appender::Screen", + name => "screenlog", + stderr => 0); + + # Assign the previously defined flexible layout + $app->layout($pattern); + + # Add the appender to a previously defined logger + $logger->add_appender($app); + + # ... and you're good to go! + $logger->debug("Blah"); + # => "2002/07/10 23:55:35 (test.pl:207)> Blah\n" + +It's also possible to remove appenders from a logger: + + $logger->remove_appender($appender_name); + +will remove an appender, specified by name, from a given logger. +Please note that this does +I<not> remove an appender from the system. + +To eradicate an appender from the system, +you need to call C<Log::Log4perl-E<gt>eradicate_appender($appender_name)> +which will first remove the appender from every logger in the system +and then will delete all references Log4perl holds to it. + +To remove a logger from the system, use +C<Log::Log4perl-E<gt>remove_logger($logger)>. After the remaining +reference C<$logger> goes away, the logger will self-destruct. If the +logger in question is a stealth logger, all of its convenience shortcuts +(DEBUG, INFO, etc) will turn into no-ops. + +=head1 How about Log::Dispatch::Config? + +Tatsuhiko Miyagawa's C<Log::Dispatch::Config> is a very clever +simplified logger implementation, covering some of the I<log4j> +functionality. Among the things that +C<Log::Log4perl> can but C<Log::Dispatch::Config> can't are: + +=over 4 + +=item * + +You can't assign categories to loggers. For small systems that's fine, +but if you can't turn off and on detailed logging in only a tiny +subsystem of your environment, you're missing out on a majorly +useful log4j feature. + +=item * + +Defining appender thresholds. Important if you want to solve problems like +"log all messages of level FATAL to STDERR, plus log all DEBUG +messages in C<Foo::Bar> to a log file". If you don't have appenders +thresholds, there's no way to prevent cluttering STDERR with DEBUG messages. + +=item * + +PatternLayout specifications in accordance with the standard +(e.g. "%d{HH:mm}"). + +=back + +Bottom line: Log::Dispatch::Config is fine for small systems with +simple logging requirements. However, if you're +designing a system with lots of subsystems which you need to control +independently, you'll love the features of C<Log::Log4perl>, +which is equally easy to use. + +=head1 Using Log::Log4perl with wrapper functions and classes + +If you don't use C<Log::Log4perl> as described above, +but from a wrapper function, the pattern layout will generate wrong data +for %F, %C, %L, and the like. Reason for this is that C<Log::Log4perl>'s +loggers assume a static caller depth to the application that's using them. + +If you're using +one (or more) wrapper functions, C<Log::Log4perl> will indicate where +your logger function called the loggers, not where your application +called your wrapper: + + use Log::Log4perl qw(:easy); + Log::Log4perl->easy_init({ level => $DEBUG, + layout => "%M %m%n" }); + + sub mylog { + my($message) = @_; + + DEBUG $message; + } + + sub func { + mylog "Hello"; + } + + func(); + +prints + + main::mylog Hello + +but that's probably not what your application expects. Rather, you'd +want + + main::func Hello + +because the C<func> function called your logging function. + +But don't despair, there's a solution: Just register your wrapper +package with Log4perl beforehand. If Log4perl then finds that it's being +called from a registered wrapper, it will automatically step up to the +next call frame. + + Log::Log4perl->wrapper_register(__PACKAGE__); + + sub mylog { + my($message) = @_; + + DEBUG $message; + } + +Alternatively, you can increase the value of the global variable +C<$Log::Log4perl::caller_depth> (defaults to 0) by one for every +wrapper that's in between your application and C<Log::Log4perl>, +then C<Log::Log4perl> will compensate for the difference: + + sub mylog { + my($message) = @_; + + local $Log::Log4perl::caller_depth = + $Log::Log4perl::caller_depth + 1; + DEBUG $message; + } + +Also, note that if you're writing a subclass of Log4perl, like + + package MyL4pWrapper; + use Log::Log4perl; + our @ISA = qw(Log::Log4perl); + +and you want to call get_logger() in your code, like + + use MyL4pWrapper; + + sub get_logger { + my $logger = Log::Log4perl->get_logger(); + } + +then the get_logger() call will get a logger for the C<MyL4pWrapper> +category, not for the package calling the wrapper class as in + + package UserPackage; + my $logger = MyL4pWrapper->get_logger(); + +To have the above call to get_logger return a logger for the +"UserPackage" category, you need to tell Log4perl that "MyL4pWrapper" +is a Log4perl wrapper class: + + use MyL4pWrapper; + Log::Log4perl->wrapper_register(__PACKAGE__); + + sub get_logger { + # Now gets a logger for the category of the calling package + my $logger = Log::Log4perl->get_logger(); + } + +This feature works both for Log4perl-relaying classes like the wrapper +described above, and for wrappers that inherit from Log4perl use Log4perl's +get_logger function via inheritance, alike. + +=head1 Access to Internals + +The following methods are only of use if you want to peek/poke in +the internals of Log::Log4perl. Be careful not to disrupt its +inner workings. + +=over 4 + +=item C<< Log::Log4perl->appenders() >> + +To find out which appenders are currently defined (not only +for a particular logger, but overall), a C<appenders()> +method is available to return a reference to a hash mapping appender +names to their Log::Log4perl::Appender object references. + +=back + +=head1 Dirty Tricks + +=over 4 + +=item infiltrate_lwp() + +The famous LWP::UserAgent module isn't Log::Log4perl-enabled. Often, though, +especially when tracing Web-related problems, it would be helpful to get +some insight on what's happening inside LWP::UserAgent. Ideally, LWP::UserAgent +would even play along in the Log::Log4perl framework. + +A call to C<Log::Log4perl-E<gt>infiltrate_lwp()> does exactly this. +In a very rude way, it pulls the rug from under LWP::UserAgent and transforms +its C<debug/conn> messages into C<debug()> calls of loggers of the category +C<"LWP::UserAgent">. Similarily, C<LWP::UserAgent>'s C<trace> messages +are turned into C<Log::Log4perl>'s C<info()> method calls. Note that this +only works for LWP::UserAgent versions E<lt> 5.822, because this (and +probably later) versions miss debugging functions entirely. + +=item Suppressing 'duplicate' LOGDIE messages + +If a script with a simple Log4perl configuration uses logdie() to catch +errors and stop processing, as in + + use Log::Log4perl qw(:easy) ; + Log::Log4perl->easy_init($DEBUG); + + shaky_function() or LOGDIE "It failed!"; + +there's a cosmetic problem: The message gets printed twice: + + 2005/07/10 18:37:14 It failed! + It failed! at ./t line 12 + +The obvious solution is to use LOGEXIT() instead of LOGDIE(), but there's +also a special tag for Log4perl that suppresses the second message: + + use Log::Log4perl qw(:no_extra_logdie_message); + +This causes logdie() and logcroak() to call exit() instead of die(). To +modify the script exit code in these occasions, set the variable +C<$Log::Log4perl::LOGEXIT_CODE> to the desired value, the default is 1. + +=item Redefine values without causing errors + +Log4perl's configuration file parser has a few basic safety mechanisms to +make sure configurations are more or less sane. + +One of these safety measures is catching redefined values. For example, if +you first write + + log4perl.category = WARN, Logfile + +and then a couple of lines later + + log4perl.category = TRACE, Logfile + +then you might have unintentionally overwritten the first value and Log4perl +will die on this with an error (suspicious configurations always throw an +error). Now, there's a chance that this is intentional, for example when +you're lumping together several configuration files and actually I<want> +the first value to overwrite the second. In this case use + + use Log::Log4perl qw(:nostrict); + +to put Log4perl in a more permissive mode. + +=item Prevent croak/confess from stringifying + +The logcroak/logconfess functions stringify their arguments before +they pass them to Carp's croak/confess functions. This can get in the +way if you want to throw an object or a hashref as an exception, in +this case use: + + $Log::Log4perl::STRINGIFY_DIE_MESSAGE = 0; + + eval { + # throws { foo => "bar" } + # without stringification + $logger->logcroak( { foo => "bar" } ); + }; + +=back + +=head1 EXAMPLE + +A simple example to cut-and-paste and get started: + + use Log::Log4perl qw(get_logger); + + my $conf = q( + log4perl.category.Bar.Twix = WARN, Logfile + log4perl.appender.Logfile = Log::Log4perl::Appender::File + log4perl.appender.Logfile.filename = test.log + log4perl.appender.Logfile.layout = \ + Log::Log4perl::Layout::PatternLayout + log4perl.appender.Logfile.layout.ConversionPattern = %d %F{1} %L> %m %n + ); + + Log::Log4perl::init(\$conf); + + my $logger = get_logger("Bar::Twix"); + $logger->error("Blah"); + +This will log something like + + 2002/09/19 23:48:15 t1 25> Blah + +to the log file C<test.log>, which Log4perl will append to or +create it if it doesn't exist already. + +=head1 INSTALLATION + +If you want to use external appenders provided with C<Log::Dispatch>, +you need to install C<Log::Dispatch> (2.00 or better) from CPAN, +which itself depends on C<Attribute-Handlers> and +C<Params-Validate>. And a lot of other modules, that's the reason +why we're now shipping Log::Log4perl with its own standard appenders +and only if you wish to use additional ones, you'll have to go through +the C<Log::Dispatch> installation process. + +Log::Log4perl needs C<Test::More>, C<Test::Harness> and C<File::Spec>, +but they already come with fairly recent versions of perl. +If not, everything's automatically fetched from CPAN if you're using the CPAN +shell (CPAN.pm), because they're listed as dependencies. + +C<Time::HiRes> (1.20 or better) is required only if you need the +fine-grained time stamps of the C<%r> parameter in +C<Log::Log4perl::Layout::PatternLayout>. + +Manual installation works as usual with + + perl Makefile.PL + make + make test + make install + +=head1 DEVELOPMENT + +Log::Log4perl is still being actively developed. We will +always make sure the test suite (approx. 500 cases) will pass, but there +might still be bugs. please check L<http://github.com/mschilli/log4perl> +for the latest release. The api has reached a mature state, we will +not change it unless for a good reason. + +Bug reports and feedback are always welcome, just email them to our +mailing list shown in the AUTHORS section. We're usually addressing +them immediately. + +=head1 REFERENCES + +=over 4 + +=item [1] + +Michael Schilli, "Retire your debugger, log smartly with Log::Log4perl!", +Tutorial on perl.com, 09/2002, +L<http://www.perl.com/pub/a/2002/09/11/log4perl.html> + +=item [2] + +Ceki Gülcü, "Short introduction to log4j", +L<http://logging.apache.org/log4j/1.2/manual.html> + +=item [3] + +Vipan Singla, "Don't Use System.out.println! Use Log4j.", +L<http://www.vipan.com/htdocs/log4jhelp.html> + +=item [4] + +The Log::Log4perl project home page: L<http://log4perl.com> + +=back + +=head1 SEE ALSO + +L<Log::Log4perl::Config|Log::Log4perl::Config>, +L<Log::Log4perl::Appender|Log::Log4perl::Appender>, +L<Log::Log4perl::Layout::PatternLayout|Log::Log4perl::Layout::PatternLayout>, +L<Log::Log4perl::Layout::SimpleLayout|Log::Log4perl::Layout::SimpleLayout>, +L<Log::Log4perl::Level|Log::Log4perl::Level>, +L<Log::Log4perl::JavaMap|Log::Log4perl::JavaMap> +L<Log::Log4perl::NDC|Log::Log4perl::NDC>, + +=head1 AUTHORS + +Please contribute patches to the project on Github: + + http://github.com/mschilli/log4perl + +Send bug reports or requests for enhancements to the authors via our + +MAILING LIST (questions, bug reports, suggestions/patches): +log4perl-devel@lists.sourceforge.net + +Authors (please contact them via the list above, not directly): +Mike Schilli <m@perlmeister.com>, +Kevin Goess <cpan@goess.org> + +Contributors (in alphabetical order): +Ateeq Altaf, Cory Bennett, Jens Berthold, Jeremy Bopp, Hutton +Davidson, Chris R. Donnelly, Matisse Enzer, Hugh Esco, Anthony +Foiani, James FitzGibbon, Carl Franks, Dennis Gregorovic, Andy +Grundman, Paul Harrington, Alexander Hartmaier, David Hull, +Robert Jacobson, Jason Kohles, Jeff Macdonald, Markus Peter, +Brett Rann, Peter Rabbitson, Erik Selberg, Aaron Straup Cope, +Lars Thegler, David Viner, Mac Yang. + +=head1 LICENSE + +Copyright 2002-2013 by Mike Schilli E<lt>m@perlmeister.comE<gt> +and Kevin Goess E<lt>cpan@goess.orgE<gt>. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + |