/lib/Log/Log4perl.pm
Perl | 2967 lines | 2370 code | 435 blank | 162 comment | 121 complexity | f06a0927ec5b06fb99d4c3cc03df6993 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- ##################################################
- package Log::Log4perl;
- ##################################################
- END { local($?); Log::Log4perl::Logger::cleanup(); }
- use 5.006;
- use strict;
- use warnings;
- use Carp;
- use Log::Log4perl::Util;
- use Log::Log4perl::Logger;
- use Log::Log4perl::Level;
- use Log::Log4perl::Config;
- use Log::Log4perl::Appender;
- our $VERSION = '1.50';
- # 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;
- } else {
- # I suggest this becomes a croak() after a
- # reasonable deprecation cycle.
- carp "All arguments to easy_init should be either "
- . "an integer log level or a hash reference.";
- }
- }
- }
- 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
- # Need to split this up in two lines, or CVS will
- # mess it up.
- 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 day
- (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 mechani…
Large files files are truncated, but you can click here to view the full file