Log::Log4perl::FAQ(3pm) - phpMan

Command: man perldoc info search(apropos)  


FAQ(3)                         User Contributed Perl Documentation                         FAQ(3)



NAME
       Log::Log4perl::FAQ - Frequently Asked Questions on Log::Log4perl

DESCRIPTION
       This FAQ shows a wide variety of commonly encountered logging tasks and how to solve them
       in the most elegant way with Log::Log4perl. Most of the time, this will be just a matter
       of smartly configuring your Log::Log4perl configuration files.

   Why use Log::Log4perl instead of any other logging module on CPAN?
       That's a good question. There's dozens of logging modules on CPAN.  When it comes to
       logging, people typically think: "Aha. Writing out debug and error messages. Debug is
       lower than error. Easy. I'm gonna write my own." Writing a logging module is like a rite
       of passage for every Perl programmer, just like writing your own templating system.

       Of course, after getting the basics right, features need to be added. You'd like to write
       a timestamp with every message. Then timestamps with microseconds. Then messages need to
       be written to both the screen and a log file.

       And, as your application grows in size you might wonder: Why doesn't my logging system
       scale along with it? You would like to switch on logging in selected parts of the
       application, and not all across the board, because this kills performance. This is when
       people turn to Log::Log4perl, because it handles all of that.

       Avoid this costly switch.

       Use "Log::Log4perl" right from the start. "Log::Log4perl"'s ":easy" mode supports easy
       logging in simple scripts:

           use Log::Log4perl qw(:easy);
           Log::Log4perl->easy_init($DEBUG);

           DEBUG "A low-level message";
           ERROR "Won't make it until level gets increased to ERROR";

       And when your application inevitably grows, your logging system grows with it without you
       having to change any code.

       Please, don't re-invent logging. "Log::Log4perl" is here, it's easy to use, it scales, and
       covers many areas you haven't thought of yet, but will enter soon.

   What's the easiest way to use Log4perl?
       If you just want to get all the comfort of logging, without much overhead, use Stealth
       Loggers. If you use Log::Log4perl in ":easy" mode like

           use Log::Log4perl qw(:easy);

       you'll have the following functions available in the current package:

           DEBUG("message");
           INFO("message");
           WARN("message");
           ERROR("message");
           FATAL("message");

       Just make sure that every package of your code where you're using them in pulls in "use
       Log::Log4perl qw(:easy)" first, then you're set.  Every stealth logger's category will be
       equivalent to the name of the package it's located in.

       These stealth loggers will be absolutely silent until you initialize Log::Log4perl in your
       main program with either

               # Define any Log4perl behavior
           Log::Log4perl->init("foo.conf");

       (using a full-blown Log4perl config file) or the super-easy method

               # Just log to STDERR
           Log::Log4perl->easy_init($DEBUG);

       or the parameter-style method with a complexity somewhat in between:

               # Append to a log file
           Log::Log4perl->easy_init( { level   => $DEBUG,
                                       file    => ">>test.log" } );

       For more info, please check out "Stealth Loggers" in Log::Log4perl.

   How can I simply log all my ERROR messages to a file?
       After pulling in the "Log::Log4perl" module, just initialize its behavior by passing in a
       configuration to its "init" method as a string reference. Then, obtain a logger instance
       and write out a message with its "error()" method:

           use Log::Log4perl qw(get_logger);

               # Define configuration
           my $conf = q(
               log4perl.logger                    = ERROR, FileApp
               log4perl.appender.FileApp          = Log::Log4perl::Appender::File
               log4perl.appender.FileApp.filename = test.log
               log4perl.appender.FileApp.layout   = PatternLayout
               log4perl.appender.FileApp.layout.ConversionPattern = %d> %m%n
           );

               # Initialize logging behavior
           Log::Log4perl->init( \$conf );

               # Obtain a logger instance
           my $logger = get_logger("Bar::Twix");
           $logger->error("Oh my, a dreadful error!");
           $logger->warn("Oh my, a dreadful warning!");

       This will append something like

           2002/10/29 20:11:55> Oh my, a dreadful error!

       to the log file "test.log". How does this all work?

       While the Log::Log4perl "init()" method typically takes the name of a configuration file
       as its input parameter like in

           Log::Log4perl->init( "/path/mylog.conf" );

       the example above shows how to pass in a configuration as text in a scalar reference.

       The configuration as shown defines a logger of the root category, which has an appender of
       type "Log::Log4perl::Appender::File" attached. The line

           log4perl.logger = ERROR, FileApp

       doesn't list a category, defining a root logger. Compare that with

           log4perl.logger.Bar.Twix = ERROR, FileApp

       which would define a logger for the category "Bar::Twix", showing probably different
       behavior. "FileApp" on the right side of the assignment is an arbitrarily defined variable
       name, which is only used to somehow reference an appender defined later on.

       Appender settings in the configuration are defined as follows:

           log4perl.appender.FileApp          = Log::Log4perl::Appender::File
           log4perl.appender.FileApp.filename = test.log

       It selects the file appender of the "Log::Log4perl::Appender" hierarchy, which will append
       to the file "test.log" if it already exists. If we wanted to overwrite a potentially
       existing file, we would have to explicitly set the appropriate
       "Log::Log4perl::Appender::File" parameter "mode":

           log4perl.appender.FileApp          = Log::Log4perl::Appender::File
           log4perl.appender.FileApp.filename = test.log
           log4perl.appender.FileApp.mode     = write

       Also, the configuration defines a PatternLayout format, adding the nicely formatted
       current date and time, an arrow (>) and a space before the messages, which is then
       followed by a newline:

           log4perl.appender.FileApp.layout   = PatternLayout
           log4perl.appender.FileApp.layout.ConversionPattern = %d> %m%n

       Obtaining a logger instance and actually logging something is typically done in a
       different system part as the Log::Log4perl initialisation section, but in this example,
       it's just done right after init for the sake of compactness:

               # Obtain a logger instance
           my $logger = get_logger("Bar::Twix");
           $logger->error("Oh my, a dreadful error!");

       This retrieves an instance of the logger of the category "Bar::Twix", which, as all other
       categories, inherits behavior from the root logger if no other loggers are defined in the
       initialization section.

       The "error()" method fires up a message, which the root logger catches. Its priority is
       equal to or higher than the root logger's priority (ERROR), which causes the root logger
       to forward it to its attached appender. By contrast, the following

           $logger->warn("Oh my, a dreadful warning!");

       doesn't make it through, because the root logger sports a higher setting (ERROR and up)
       than the WARN priority of the message.

   How can I install Log::Log4perl on Microsoft Windows?
       You can install Log::Log4perl using the CPAN client.

       Alternatively you can install it using

           ppm install Log-Log4perl

       if you're using ActiveState perl.

       That's it! Afterwards, just create a Perl script like

           use Log::Log4perl qw(:easy);
           Log::Log4perl->easy_init($DEBUG);

           my $logger = get_logger("Twix::Bar");
           $logger->debug("Watch me!");

       and run it. It should print something like

           2002/11/06 01:22:05 Watch me!

       If you find that something doesn't work, please let us know at
       log4perl-devel AT lists.net -- we'll appreciate it. Have fun!

   How can I include global (thread-specific) data in my log messages?
       Say, you're writing a web application and want all your log messages to include the
       current client's IP address. Most certainly, you don't want to include it in each and
       every log message like in

           $logger->debug( $r->connection->remote_ip,
                           " Retrieving user data from DB" );

       do you? Instead, you want to set it in a global data structure and have Log::Log4perl
       include it automatically via a PatternLayout setting in the configuration file:

           log4perl.appender.FileApp.layout.ConversionPattern = %X{ip} %m%n

       The conversion specifier %X{ip} references an entry under the key "ip" in the global "MDC"
       (mapped diagnostic context) table, which you've set once via

           Log::Log4perl::MDC->put("ip", $r->connection->remote_ip);

       at the start of the request handler. Note that this is a static (class) method, there's no
       logger object involved.  You can use this method with as many key/value pairs as you like
       as long as you reference them under different names.

       The mappings are stored in a global hash table within Log::Log4perl.  Luckily, because the
       thread model in 5.8.0 doesn't share global variables between threads unless they're
       explicitly marked as such, there's no problem with multi-threaded environments.

       For more details on the MDC, please refer to "Mapped Diagnostic Context (MDC)" in
       Log::Log4perl and Log::Log4perl::MDC.

   My application is already logging to a file. How can I duplicate all messages to also go to
       the screen?
       Assuming that you already have a Log4perl configuration file like

           log4perl.logger                    = DEBUG, FileApp

           log4perl.appender.FileApp          = Log::Log4perl::Appender::File
           log4perl.appender.FileApp.filename = test.log
           log4perl.appender.FileApp.layout   = PatternLayout
           log4perl.appender.FileApp.layout.ConversionPattern = %d> %m%n

       and log statements all over your code, it's very easy with Log4perl to have the same
       messages both printed to the logfile and the screen. No reason to change your code, of
       course, just add another appender to the configuration file and you're done:

           log4perl.logger                    = DEBUG, FileApp, ScreenApp

           log4perl.appender.FileApp          = Log::Log4perl::Appender::File
           log4perl.appender.FileApp.filename = test.log
           log4perl.appender.FileApp.layout   = PatternLayout
           log4perl.appender.FileApp.layout.ConversionPattern = %d> %m%n

           log4perl.appender.ScreenApp          = Log::Log4perl::Appender::Screen
           log4perl.appender.ScreenApp.stderr   = 0
           log4perl.appender.ScreenApp.layout   = PatternLayout
           log4perl.appender.ScreenApp.layout.ConversionPattern = %d> %m%n

       The configuration file above is assuming that both appenders are active in the same logger
       hierarchy, in this case the "root" category.  But even if you've got file loggers defined
       in several parts of your system, belonging to different logger categories, each logging to
       different files, you can gobble up all logged messages by defining a root logger with a
       screen appender, which would duplicate messages from all your file loggers to the screen
       due to Log4perl's appender inheritance. Check

           http://www.perl.com/pub/a/2002/09/11/log4perl.html

       for details. Have fun!

   How can I make sure my application logs a message when it dies unexpectedly?
       Whenever you encounter a fatal error in your application, instead of saying something like

           open FILE, "<blah" or die "Can't open blah -- bailing out!";

       just use Log::Log4perl's fatal functions instead:

           my $log = get_logger("Some::Package");
           open FILE, "<blah" or $log->logdie("Can't open blah -- bailing out!");

       This will both log the message with priority FATAL according to your current Log::Log4perl
       configuration and then call Perl's "die()" afterwards to terminate the program. It works
       the same with stealth loggers (see "Stealth Loggers" in Log::Log4perl), all you need to do
       is call

           use Log::Log4perl qw(:easy);
           open FILE, "<blah" or LOGDIE "Can't open blah -- bailing out!";

       What can you do if you're using some library which doesn't use Log::Log4perl and calls
       "die()" internally if something goes wrong? Use a $SIG{__DIE__} pseudo signal handler

           use Log::Log4perl qw(get_logger);

           $SIG{__DIE__} = sub {
               if($^S) {
                   # We're in an eval {} and don't want log
                   # this message but catch it later
                   return;
               }
               local $Log::Log4perl::caller_depth =
                     $Log::Log4perl::caller_depth + 1;
               my $logger = get_logger("");
               $logger->fatal(@_);
               die @_; # Now terminate really
           };

       This will catch every "die()"-Exception of your application or the modules it uses. In
       case you want to It will fetch a root logger and pass on the "die()"-Message to it.  If
       you make sure you've configured with a root logger like this:

           Log::Log4perl->init(\q{
               log4perl.category         = FATAL, Logfile
               log4perl.appender.Logfile = Log::Log4perl::Appender::File
               log4perl.appender.Logfile.filename = fatal_errors.log
               log4perl.appender.Logfile.layout = \
                          Log::Log4perl::Layout::PatternLayout
               log4perl.appender.Logfile.layout.ConversionPattern = %F{1}-%L (%M)> %m%n
           });

       then all "die()" messages will be routed to a file properly. The line

            local $Log::Log4perl::caller_depth =
                  $Log::Log4perl::caller_depth + 1;

       in the pseudo signal handler above merits a more detailed explanation. With the setup
       above, if a module calls "die()" in one of its functions, the fatal message will be logged
       in the signal handler and not in the original function -- which will cause the %F, %L and
       %M placeholders in the pattern layout to be replaced by the filename, the line number and
       the function/method name of the signal handler, not the error-throwing module. To adjust
       this, Log::Log4perl has the $caller_depth variable, which defaults to 0, but can be set to
       positive integer values to offset the caller level. Increasing it by one will cause it to
       log the calling function's parameters, not the ones of the signal handler.  See "Using
       Log::Log4perl from wrapper classes" in Log::Log4perl for more details.

   How can I hook up the LWP library with Log::Log4perl?
       Or, to put it more generally: How can you utilize a third-party library's embedded logging
       and debug statements in Log::Log4perl?  How can you make them print to configurable
       appenders, turn them on and off, just as if they were regular Log::Log4perl logging
       statements?

       The easiest solution is to map the third-party library logging statements to
       Log::Log4perl's stealth loggers via a typeglob assignment.

       As an example, let's take LWP, one of the most popular Perl modules, which makes handling
       WWW requests and responses a breeze.  Internally, LWP uses its own logging and debugging
       system, utilizing the following calls inside the LWP code (from the LWP::Debug man page):

               # Function tracing
           LWP::Debug::trace('send()');

               # High-granular state in functions
           LWP::Debug::debug('url ok');

               # Data going over the wire
           LWP::Debug::conns("read $n bytes: $data");

       First, let's assign Log::Log4perl priorities to these functions: I'd suggest that
       "debug()" messages have priority "INFO", "trace()" uses "DEBUG" and "conns()" also logs
       with "DEBUG" -- although your mileage may certainly vary.

       Now, in order to transpartently hook up LWP::Debug with Log::Log4perl, all we have to do
       is say

           package LWP::Debug;
           use Log::Log4perl qw(:easy);

           *trace = *INFO;
           *conns = *DEBUG;
           *debug = *DEBUG;

           package main;
           # ... go on with your regular program ...

       at the beginning of our program. In this way, every time the, say, "LWP::UserAgent" module
       calls "LWP::Debug::trace()", it will implicitely call INFO(), which is the "info()" method
       of a stealth logger defined for the Log::Log4perl category "LWP::Debug". Is this cool or
       what?

       Here's a complete program:

           use LWP::UserAgent;
           use HTTP::Request::Common;
           use Log::Log4perl qw(:easy);

           Log::Log4perl->easy_init(
               { category => "LWP::Debug",
                 level    => $DEBUG,
                 layout   => "%r %p %M-%L %m%n",
               });

           package LWP::Debug;
           use Log::Log4perl qw(:easy);
           *trace = *INFO;
           *conns = *DEBUG;
           *debug = *DEBUG;

           package main;
           my $ua = LWP::UserAgent->new();
           my $resp = $ua->request(GET "http://amazon.com");

           if($resp->is_success()) {
               print "Success: Received ",
                     length($resp->content()), "\n";
           } else {
               print "Error: ", $resp->code(), "\n";
           }

       This will generate the following output on STDERR:

           174 INFO LWP::UserAgent::new-164 ()
           208 INFO LWP::UserAgent::request-436 ()
           211 INFO LWP::UserAgent::send_request-294 GET http://amazon.com
           212 DEBUG LWP::UserAgent::_need_proxy-1123 Not proxied
           405 INFO LWP::Protocol::http::request-122 ()
           859 DEBUG LWP::Protocol::collect-206 read 233 bytes
           863 DEBUG LWP::UserAgent::request-443 Simple response: Found
           869 INFO LWP::UserAgent::request-436 ()
           871 INFO LWP::UserAgent::send_request-294
            GET http://www.amazon.com:80/exec/obidos/gateway_redirect
           872 DEBUG LWP::UserAgent::_need_proxy-1123 Not proxied
           873 INFO LWP::Protocol::http::request-122 ()
           1016 DEBUG LWP::UserAgent::request-443 Simple response: Found
           1020 INFO LWP::UserAgent::request-436 ()
           1022 INFO LWP::UserAgent::send_request-294
            GET http://www.amazon.com/exec/obidos/subst/home/home.html/
           1023 DEBUG LWP::UserAgent::_need_proxy-1123 Not proxied
           1024 INFO LWP::Protocol::http::request-122 ()
           1382 DEBUG LWP::Protocol::collect-206 read 632 bytes
           ...
           2605 DEBUG LWP::Protocol::collect-206 read 77 bytes
           2607 DEBUG LWP::UserAgent::request-443 Simple response: OK
           Success: Received 42584

       Of course, in this way, the embedded logging and debug statements within LWP can be
       utilized in any Log::Log4perl way you can think of. You can have them sent to different
       appenders, block them based on the category and everything else Log::Log4perl has to
       offer.

       Only drawback of this method: Steering logging behavior via category is always based on
       the "LWP::Debug" package. Although the logging statements reflect the package name of the
       issuing module properly, the stealth loggers in "LWP::Debug" are all of the category
       "LWP::Debug".  This implies that you can't control the logging behavior based on the
       package that's initiating a log request (e.g. LWP::UserAgent) but only based on the
       package that's actually executing the logging statement, "LWP::Debug" in this case.

       To work around this conundrum, we need to write a wrapper function and plant it into the
       "LWP::Debug" package. It will determine the caller and create a logger bound to a category
       with the same name as the caller's package:

           package LWP::Debug;

           use Log::Log4perl qw(:levels get_logger);

           sub l4p_wrapper {
               my($prio, @message) = @_;
               $Log::Log4perl::caller_depth += 2;
               get_logger(scalar caller(1))->log($prio, @message);
               $Log::Log4perl::caller_depth -= 2;
           }

           no warnings 'redefine';
           *trace = sub { l4p_wrapper($INFO, @_); };
           *debug = *conns = sub { l4p_wrapper($DEBUG, @_); };

           package main;
           # ... go on with your main program ...

       This is less performant than the previous approach, because every log request will request
       a reference to a logger first, then call the wrapper, which will in turn call the
       appropriate log function.

       This hierarchy shift has to be compensated for by increasing $Log::Log4perl::caller_depth
       by 2 before calling the log function and decreasing it by 2 right afterwards. Also, the
       "l4p_wrapper" function shown above calls caller(1) which determines the name of the
       package two levels down the calling hierarchy (and therefore compensates for both the
       wrapper function and the anonymous subroutine calling it).

       "no warnings 'redefine'" suppresses a warning Perl would generate otherwise upon
       redefining "LWP::Debug"'s "trace()", "debug()" and "conns()" functions. In case you use a
       perl prior to 5.6.x, you need to manipulate $^W instead.

       To make things easy for you when dealing with LWP, Log::Log4perl 0.47 introduces
       "Log::Log4perl->infiltrate_lwp()" which does exactly the above.

   What if I need dynamic values in a static Log4perl configuration file?
       Say, your application uses Log::Log4perl for logging and therefore comes with a Log4perl
       configuration file, specifying the logging behavior.  But, you also want it to take
       command line parameters to set values like the name of the log file.  How can you have
       both a static Log4perl configuration file and a dynamic command line interface?

       As of Log::Log4perl 0.28, every value in the configuration file can be specified as a Perl
       hook. So, instead of saying

           log4perl.appender.Logfile.filename = test.log

       you could just as well have a Perl subroutine deliver the value dynamically:

           log4perl.appender.Logfile.filename = sub { logfile(); };

       given that "logfile()" is a valid function in your "main" package returning a string
       containing the path to the log file.

       Or, think about using the value of an environment variable:

           log4perl.appender.DBI.user = sub { $ENV{USERNAME} };

       When "Log::Log4perl->init()" parses the configuration file, it will notice the assignment
       above because of its "sub {...}" pattern and treat it in a special way: It will evaluate
       the subroutine (which can contain arbitrary Perl code) and take its return value as the
       right side of the assignment.

       A typical application would be called like this on the command line:

           app                # log file is "test.log"
           app -l mylog.txt   # log file is "mylog.txt"

       Here's some sample code implementing the command line interface above:

           use Log::Log4perl qw(get_logger);
           use Getopt::Std;

           getopt('l:', \our %OPTS);

           my $conf = q(
           log4perl.category.Bar.Twix         = WARN, Logfile
           log4perl.appender.Logfile          = Log::Log4perl::Appender::File
           log4perl.appender.Logfile.filename = sub { logfile(); };
           log4perl.appender.Logfile.layout   = SimpleLayout
           );

           Log::Log4perl::init(\$conf);

           my $logger = get_logger("Bar::Twix");
           $logger->error("Blah");

           ###########################################
           sub logfile {
           ###########################################
               if(exists $OPTS{l}) {
                   return $OPTS{l};
               } else {
                   return "test.log";
               }
           }

       Every Perl hook may contain arbitrary perl code, just make sure to fully qualify eventual
       variable names (e.g. %main::OPTS instead of %OPTS).

       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(). This will prevent Log::Log4perl from executing any Perl code in
       the config file (including code for custom conversion specifiers (see "Custom cspecs" in
       Log::Log4perl::Layout::PatternLayout).

   How can I roll over my logfiles automatically at midnight?
       Long-running applications tend to produce ever-increasing logfiles.  For backup and
       cleanup purposes, however, it is often desirable to move the current logfile to a
       different location from time to time and start writing a new one.

       This is a non-trivial task, because it has to happen in sync with the logging system in
       order not to lose any messages in the process.

       Luckily, Mark Pfeiffer's "Log::Dispatch::FileRotate" appender works well with
       Log::Log4perl to rotate your logfiles in a variety of ways.

       Note, however, that having the application deal with rotating a log file is not cheap.
       Among other things, it requires locking the log file with every write to avoid race
       conditions.  There are good reasons to use external rotators like "newsyslog" instead.
       See the entry "How can I rotate a logfile with newsyslog?" in the FAQ for more information
       on how to configure it.

       When using "Log::Dispatch::FileRotate", all you have to do is specify it in your
       Log::Log4perl configuration file and your logfiles will be rotated automatically.

       You can choose between rolling based on a maximum size ("roll if greater than 10 MB") or
       based on a date pattern ("roll everyday at midnight").  In both cases,
       "Log::Dispatch::FileRotate" allows you to define a number "max" of saved files to keep
       around until it starts overwriting the oldest ones. If you set the "max" parameter to 2
       and the name of your logfile is "test.log", "Log::Dispatch::FileRotate" will move
       "test.log" to "test.log.1" on the first rollover. On the second rollover, it will move
       "test.log.1" to "test.log.2" and then "test.log" to "test.log.1". On the third rollover,
       it will move "test.log.1" to "test.log.2" (therefore discarding the old "test.log.2") and
       "test.log" to "test.log.1". And so forth. This way, there's always going to be a maximum
       of 2 saved log files around.

       Here's an example of a Log::Log4perl configuration file, defining a daily rollover at
       midnight (date pattern "yyyy-MM-dd"), keeping a maximum of 5 saved logfiles around:

           log4perl.category         = WARN, Logfile
           log4perl.appender.Logfile = Log::Dispatch::FileRotate
           log4perl.appender.Logfile.filename    = test.log
           log4perl.appender.Logfile.max         = 5
           log4perl.appender.Logfile.DatePattern = yyyy-MM-dd
           log4perl.appender.Logfile.TZ          = PST
           log4perl.appender.Logfile.layout = \
               Log::Log4perl::Layout::PatternLayout
           log4perl.appender.Logfile.layout.ConversionPattern = %d %m %n

       Please see the "Log::Dispatch::FileRotate" documentation for details.
       "Log::Dispatch::FileRotate" is available on CPAN.

   What's the easiest way to turn off all logging, even with a lengthy Log4perl configuration
       file?
       In addition to category-based levels and appender thresholds, Log::Log4perl supports
       system-wide logging thresholds. This is the minimum level the system will require of any
       logging events in order for them to make it through to any configured appenders.

       For example, putting the line

           log4perl.threshold = ERROR

       anywhere in your configuration file will limit any output to any appender to events with
       priority of ERROR or higher (ERROR or FATAL that is).

       However, in order to suppress all logging entirely, you need to use a priority that's
       higher than FATAL: It is simply called "OFF", and it is never used by any logger. By
       definition, it is higher than the highest defined logger level.

       Therefore, if you keep the line

           log4perl.threshold = OFF

       somewhere in your Log::Log4perl configuration, the system will be quiet as a graveyard. If
       you deactivate the line (e.g. by commenting it out), the system will, upon config reload,
       snap back to normal operation, providing logging messages according to the rest of the
       configuration file again.

   I keep getting duplicate log messages! What's wrong?
       Having several settings for related categories in the Log4perl configuration file
       sometimes leads to a phenomenon called "message duplication". It can be very confusing at
       first, but if thought through properly, it turns out that Log4perl behaves as advertised.
       But, don't despair, of course there's a number of ways to avoid message duplication in
       your logs.

       Here's a sample Log4perl configuration file that produces the phenomenon:

           log4perl.logger.Cat        = ERROR, Screen
           log4perl.logger.Cat.Subcat = WARN, Screen

           log4perl.appender.Screen   = Log::Log4perl::Appender::Screen
           log4perl.appender.Screen.layout = SimpleLayout

       It defines two loggers, one for category "Cat" and one for "Cat::Subcat", which is
       obviously a subcategory of "Cat".  The parent logger has a priority setting of ERROR, the
       child is set to the lower "WARN" level.

       Now imagine the following code in your program:

           my $logger = get_logger("Cat.Subcat");
           $logger->warn("Warning!");

       What do you think will happen? An unexperienced Log4perl user might think: "Well, the
       message is being sent with level WARN, so the "Cat::Subcat" logger will accept it and
       forward it to the attached "Screen" appender. Then, the message will percolate up the
       logger hierarchy, find the "Cat" logger, which will suppress the message because of its
       ERROR setting."  But, perhaps surprisingly, what you'll get with the code snippet above is
       not one but two log messages written to the screen:

           WARN - Warning!
           WARN - Warning!

       What happened? The culprit is that once the logger "Cat::Subcat" decides to fire, it will
       forward the message unconditionally to all directly or indirectly attached appenders. The
       "Cat" logger will never be asked if it wants the message or not -- the message will just
       be pushed through to the appender attached to "Cat".

       One way to prevent the message from bubbling up the logger hierarchy is to set the
       "additivity" flag of the subordinate logger to 0:

           log4perl.logger.Cat            = ERROR, Screen
           log4perl.logger.Cat.Subcat     = WARN, Screen
           log4perl.additivity.Cat.Subcat = 0

           log4perl.appender.Screen   = Log::Log4perl::Appender::Screen
           log4perl.appender.Screen.layout = SimpleLayout

       The message will now be accepted by the "Cat::Subcat" logger, forwarded to its appender,
       but then "Cat::Subcat" will suppress any further action. While this setting avoids
       duplicate messages as seen before, it is often not the desired behavior. Messages
       percolating up the hierarchy are a useful Log4perl feature.

       If you're defining different appenders for the two loggers, one other option is to define
       an appender threshold for the higher-level appender. Typically it is set to be equal to
       the logger's level setting:

           log4perl.logger.Cat           = ERROR, Screen1
           log4perl.logger.Cat.Subcat    = WARN, Screen2

           log4perl.appender.Screen1   = Log::Log4perl::Appender::Screen
           log4perl.appender.Screen1.layout = SimpleLayout
           log4perl.appender.Screen1.Threshold = ERROR

           log4perl.appender.Screen2   = Log::Log4perl::Appender::Screen
           log4perl.appender.Screen2.layout = SimpleLayout

       Since the "Screen1" appender now blocks every message with a priority less than ERROR,
       even if the logger in charge lets it through, the message percolating up the hierarchy is
       being blocked at the last minute and not appended to "Screen1".

       So far, we've been operating well within the boundaries of the Log4j standard, which
       Log4perl adheres to. However, if you would really, really like to use a single appender
       and keep the message percolation intact without having to deal with message duplication,
       there's a non-standard solution for you:

           log4perl.logger.Cat        = ERROR, Screen
           log4perl.logger.Cat.Subcat = WARN, Screen

           log4perl.appender.Screen   = Log::Log4perl::Appender::Screen
           log4perl.appender.Screen.layout = SimpleLayout

           log4perl.oneMessagePerAppender = 1

       The "oneMessagePerAppender" flag will suppress duplicate messages to the same appender.
       Again, that's non-standard. But way cool :).

   How can I configure Log::Log4perl to send me email if something happens?
       Some incidents require immediate action. You can't wait until someone checks the log
       files, you need to get notified on your pager right away.

       The easiest way to do that is by using the "Log::Dispatch::Email::MailSend" module as an
       appender. It comes with the "Log::Dispatch" bundle and allows you to specify recipient and
       subject of outgoing emails in the Log4perl configuration file:

           log4perl.category = FATAL, Mailer
           log4perl.appender.Mailer         = Log::Dispatch::Email::MailSend
           log4perl.appender.Mailer.to      = drone AT pageme.net
           log4perl.appender.Mailer.subject = Something's broken!
           log4perl.appender.Mailer.layout  = SimpleLayout

       The message of every log incident this appender gets will then be forwarded to the given
       email address. Check the "Log::Dispatch::Email::MailSend" documentation for details. And
       please make sure there's not a flood of email messages sent out by your application,
       filling up the receipient's inbox.

       There's one caveat you need to know about: The "Log::Dispatch::Email" hierarchy of
       appenders turns on buffering by default. This means that the appender will not send out
       messages right away but wait until a certain threshold has been reached. If you'd rather
       have your alerts sent out immeditately, use

           log4perl.appender.Mailer.buffered = 0

       to turn buffering off.

   How can I write my own appender?
       First off, Log::Log4perl comes with a set of standard appenders. Then, there's a lot of
       Log4perl-compatible appenders already available on CPAN: Just run a search for
       "Log::Dispatch" on http://search.cpan.org and chances are that what you're looking for has
       already been developed, debugged and been used successfully in production -- no need for
       you to reinvent the wheel.

       Also, Log::Log4perl ships with a nifty database appender named
       Log::Log4perl::Appender::DBI -- check it out if talking to databases is your desire.

       But if you're up for a truly exotic task, you might have to write an appender yourself.
       That's very easy -- it takes no longer than a couple of minutes.

       Say, we wanted to create an appender of the class "ColorScreenAppender", which logs
       messages to the screen in a configurable color. Just create a new class in
       "ColorScreenAppender.pm":

           package ColorScreenAppender;

       Now let's assume that your Log::Log4perl configuration file "test.conf" looks like this:

           log4perl.logger = INFO, ColorApp

           log4perl.appender.ColorApp=ColorScreenAppender
           log4perl.appender.ColorApp.color=blue

           log4perl.appender.ColorApp.layout = PatternLayout
           log4perl.appender.ColorApp.layout.ConversionPattern=%d %m %n

       This will cause Log::Log4perl on "init()" to look for a class ColorScreenAppender and call
       its constructor new(). Let's add new() to ColorScreenAppender.pm:

           sub new {
               my($class, %options) = @_;

               my $self = { %options };
               bless $self, $class;

               return $self;
           }

       To initialize this appender, Log::Log4perl will call and pass all attributes of the
       appender as defined in the configuration file to the constructor as name/value pairs (in
       this case just one):

           ColorScreenAppender->new(color => "blue");

       The new() method listed above stores the contents of the %options hash in the object's
       instance data hash (referred to by $self).  That's all for initializing a new appender
       with Log::Log4perl.

       Second, ColorScreenAppender needs to expose a "log()" method, which will be called by
       Log::Log4perl every time it thinks the appender should fire. Along with the object
       reference (as usual in Perl's object world), log() will receive a list of name/value
       pairs, of which only the one under the key "message" shall be of interest for now since it
       is the message string to be logged. At this point, Log::Log4perl has already taken care of
       joining the message to be a single string.

       For our special appender ColorScreenAppender, we're using the Term::ANSIColor module to
       colorize the output:

           use Term::ANSIColor;

           sub log {
               my($self, %params) = @_;

               print colored($params{message},
                             $self->{color});
           }

       The color (as configured in the Log::Log4perl configuration file) is available as
       $self->{color} in the appender object. Don't forget to return

           1;

       at the end of ColorScreenAppender.pm and you're done. Install the new appender somewhere
       where perl can find it and try it with a test script like

           use Log::Log4perl qw(:easy);
           Log::Log4perl->init("test.conf");
           ERROR("blah");

       to see the new colored output. Is this cool or what?

       And it gets even better: You can write dynamically generated appender classes using the
       "Class::Prototyped" module. Here's an example of an appender prepending every outgoing
       message with a configurable number of bullets:

           use Class::Prototyped;

           my $class = Class::Prototyped->newPackage(
             "MyAppenders::Bulletizer",
             bullets => 1,
             log     => sub {
               my($self, %params) = @_;
               print "*" x $self->bullets(),
                     $params{message};
             },
           );

           use Log::Log4perl qw(:easy);

           Log::Log4perl->init(\ q{
             log4perl.logger = INFO, Bully

             log4perl.appender.Bully=MyAppenders::Bulletizer
             log4perl.appender.Bully.bullets=3

             log4perl.appender.Bully.layout = PatternLayout
             log4perl.appender.Bully.layout.ConversionPattern=%m %n
           });

               # ... prints: "***Boo!\n";
           INFO "Boo!";

   How can I drill down on references before logging them?
       If you've got a reference to a nested structure or object, then you probably don't want to
       log it as "HASH(0x81141d4)" but rather dump it as something like

           $VAR1 = {
                     'a' => 'b',
                     'd' => 'e'
                   };

       via a module like Data::Dumper. While it's syntactically correct to say

           $logger->debug(Data::Dumper::Dumper($ref));

       this call imposes a huge performance penalty on your application if the message is
       suppressed by Log::Log4perl, because Data::Dumper will perform its expensive operations in
       any case, because it doesn't know that its output will be thrown away immediately.

       As of Log::Log4perl 0.28, there's a better way: Use the message output filter format as in

           $logger->debug( {filter => \&Data::Dumper::Dumper,
                            value  => $ref} );

       and Log::Log4perl won't call the filter function unless the message really gets written
       out to an appender. Just make sure to pass the whole slew as a reference to a hash
       specifying a filter function (as a sub reference) under the key "filter" and the value to
       be passed to the filter function in "value").  When it comes to logging, Log::Log4perl
       will call the filter function, pass the "value" as an argument and log the return value.
       Saves you serious cycles.

   How can I collect all FATAL messages in an extra log file?
       Suppose you have employed Log4perl all over your system and you've already activated
       logging in various subsystems. On top of that, without disrupting any other settings, how
       can you collect all FATAL messages all over the system and send them to a separate log
       file?

       If you define a root logger like this:

           log4perl.logger                  = FATAL, File
           log4perl.appender.File           = Log::Log4perl::Appender::File
           log4perl.appender.File.filename  = /tmp/fatal.txt
           log4perl.appender.File.layout    = PatternLayout
           log4perl.appender.File.layout.ConversionPattern= %d %m %n
               # !!! Something's missing ...

       you'll be surprised to not only receive all FATAL messages issued anywhere in the system,
       but also everything else -- gazillions of ERROR, WARN, INFO and even DEBUG messages will
       end up in your fatal.txt logfile!  Reason for this is Log4perl's (or better: Log4j's)
       appender additivity.  Once a lower-level logger decides to fire, the message is going to
       be forwarded to all appenders upstream -- without further priority checks with their
       attached loggers.

       There's a way to prevent this, however: If your appender defines a minimum threshold, only
       messages of this priority or higher are going to be logged. So, just add

           log4perl.appender.File.Threshold = FATAL

       to the configuration above, and you'll get what you wanted in the first place: An overall
       system FATAL message collector.

   How can I bundle several log messages into one?
       Would you like to tally the messages arriving at your appender and dump out a summary once
       they're exceeding a certain threshold?  So that something like

           $logger->error("Blah");
           $logger->error("Blah");
           $logger->error("Blah");

       won't be logged as

           Blah
           Blah
           Blah

       but as

           [3] Blah

       instead? If you'd like to hold off on logging a message until it has been sent a couple of
       times, you can roll that out by creating a buffered appender.

       Let's define a new appender like

           package TallyAppender;

           sub new {
               my($class, %options) = @_;

               my $self = { maxcount => 5,
                            %options
                          };

               bless $self, $class;

               $self->{last_message}        = "";
               $self->{last_message_count}  = 0;

               return $self;
           }

       with two additional instance variables "last_message" and "last_message_count", storing
       the content of the last message sent and a counter of how many times this has happened.
       Also, it features a configuration parameter "maxcount" which defaults to 5 in the snippet
       above but can be set in the Log4perl configuration file like this:

           log4perl.logger = INFO, A
           log4perl.appender.A=TallyAppender
           log4perl.appender.A.maxcount = 3

       The main tallying logic lies in the appender's "log" method, which is called every time
       Log4perl thinks a message needs to get logged by our appender:

           sub log {
               my($self, %params) = @_;

                   # Message changed? Print buffer.
               if($self->{last_message} and
                  $params{message} ne $self->{last_message}) {
                   print "[$self->{last_message_count}]: " .
                         "$self->{last_message}";
                   $self->{last_message_count} = 1;
                   $self->{last_message} = $params{message};
                   return;
               }

               $self->{last_message_count}++;
               $self->{last_message} = $params{message};

                   # Threshold exceeded? Print, reset counter
               if($self->{last_message_count} >=
                  $self->{maxcount}) {
                   print "[$self->{last_message_count}]: " .
                         "$params{message}";
                   $self->{last_message_count} = 0;
                   $self->{last_message}       = "";
                   return;
               }
           }

       We basically just check if the oncoming message in $param{message} is equal to what we've
       saved before in the "last_message" instance variable. If so, we're increasing
       "last_message_count".  We print the message in two cases: If the new message is different
       than the buffered one, because then we need to dump the old stuff and store the new. Or,
       if the counter exceeds the threshold, as defined by the "maxcount" configuration
       parameter.

       Please note that the appender always gets the fully rendered message and just compares it
       as a whole -- so if there's a date/timestamp in there, that might confuse your logic. You
       can work around this by specifying %m %n as a layout and add the date later on in the
       appender. Or, make the comparison smart enough to omit the date.

       At last, don't forget what happens if the program is being shut down.  If there's still
       messages in the buffer, they should be printed out at that point. That's easy to do in the
       appender's DESTROY method, which gets called at object destruction time:

           sub DESTROY {
               my($self) = @_;

               if($self->{last_message_count}) {
                   print "[$self->{last_message_count}]: " .
                         "$self->{last_message}";
                   return;
               }
           }

       This will ensure that none of the buffered messages are lost.  Happy buffering!

   I want to log ERROR and WARN messages to different files! How can I do that?
       Let's assume you wanted to have each logging statement written to a different file, based
       on the statement's priority. Messages with priority "WARN" are supposed to go to
       "/tmp/app.warn", events prioritized as "ERROR" should end up in "/tmp/app.error".

       Now, if you define two appenders "AppWarn" and "AppError" and assign them both to the root
       logger, messages bubbling up from any loggers below will be logged by both appenders
       because of Log4perl's message propagation feature. If you limit their exposure via the
       appender threshold mechanism and set "AppWarn"'s threshold to "WARN" and "AppError"'s to
       "ERROR", you'll still get "ERROR" messages in "AppWarn", because "AppWarn"'s "WARN"
       setting will just filter out messages with a lower priority than "WARN" -- "ERROR" is
       higher and will be allowed to pass through.

       What we need for this is a Log4perl Custom Filter, available with Log::Log4perl 0.30.

       Both appenders need to verify that the priority of the oncoming messages exactly matches
       the priority the appender is supposed to log messages of. To accomplish this task, let's
       define two custom filters, "MatchError" and "MatchWarn", which, when attached to their
       appenders, will limit messages passed on to them to those matching a given priority:

           log4perl.logger = WARN, AppWarn, AppError

               # Filter to match level ERROR
           log4perl.filter.MatchError = Log::Log4perl::Filter::LevelMatch
           log4perl.filter.MatchError.LevelToMatch  = ERROR
           log4perl.filter.MatchError.AcceptOnMatch = true

               # Filter to match level WARN
           log4perl.filter.MatchWarn  = Log::Log4perl::Filter::LevelMatch
           log4perl.filter.MatchWarn.LevelToMatch  = WARN
           log4perl.filter.MatchWarn.AcceptOnMatch = true

               # Error appender
           log4perl.appender.AppError = Log::Log4perl::Appender::File
           log4perl.appender.AppError.filename = /tmp/app.err
           log4perl.appender.AppError.layout   = SimpleLayout
           log4perl.appender.AppError.Filter   = MatchError

               # Warning appender
           log4perl.appender.AppWarn = Log::Log4perl::Appender::File
           log4perl.appender.AppWarn.filename = /tmp/app.warn
           log4perl.appender.AppWarn.layout   = SimpleLayout
           log4perl.appender.AppWarn.Filter   = MatchWarn

       The appenders "AppWarn" and "AppError" defined above are logging to "/tmp/app.warn" and
       "/tmp/app.err" respectively and have the custom filters "MatchWarn" and "MatchError"
       attached.  This setup will direct all WARN messages, issued anywhere in the system, to
       /tmp/app.warn (and ERROR messages to /tmp/app.error) -- without any overlaps.

   On our server farm, Log::Log4perl configuration files differ slightly from host to host. Can I
       roll them all into one?
       You sure can, because Log::Log4perl allows you to specify attribute values dynamically.
       Let's say that one of your appenders expects the host's IP address as one of its
       attributes. Now, you could certainly roll out different configuration files for every host
       and specify the value like

           log4perl.appender.MyAppender    = Log::Log4perl::Appender::SomeAppender
           log4perl.appender.MyAppender.ip = 10.0.0.127

       but that's a maintenance nightmare. Instead, you can have Log::Log4perl figure out the IP
       address at configuration time and set the appender's value correctly:

               # Set the IP address dynamically
           log4perl.appender.MyAppender    = Log::Log4perl::Appender::SomeAppender
           log4perl.appender.MyAppender.ip = sub { \
              use Sys::Hostname; \
              use Socket; \
              return inet_ntoa(scalar gethostbyname hostname); \
           }

       If Log::Log4perl detects that an attribute value starts with something like "sub {...", it
       will interpret it as a perl subroutine which is to be executed once at configuration time
       (not runtime!) and its return value is to be used as the attribute value. This comes in
       handy for rolling out applications whichs Log::Log4perl configuration files show small
       host-specific differences, because you can deploy the unmodified application distribution
       on all instances of the server farm.

   Log4perl doesn't interpret my backslashes correctly!
       If you're using Log4perl's feature to specify the configuration as a string in your
       program (as opposed to a separate configuration file), chances are that you've written it
       like this:

           # *** WRONG! ***

           Log::Log4perl->init( \ <<END_HERE);
               log4perl.logger = WARN, A1
               log4perl.appender.A1 = Log::Log4perl::Appender::Screen
               log4perl.appender.A1.layout = \
                   Log::Log4perl::Layout::PatternLayout
               log4perl.appender.A1.layout.ConversionPattern = %m%n
           END_HERE

           # *** WRONG! ***

       and you're getting the following error message:

           Layout not specified for appender A1 at .../Config.pm line 342.

       What's wrong? The problem is that you're using a here-document with substitution enabled
       ("<<END_HERE") and that Perl won't interpret backslashes at line-ends as continuation
       characters but will essentially throw them out. So, in the code above, the layout line
       will look like

           log4perl.appender.A1.layout =

       to Log::Log4perl which causes it to report an error. To interpret the backslash at the end
       of the line correctly as a line-continuation character, use the non-interpreting mode of
       the here-document like in

           # *** RIGHT! ***

           Log::Log4perl->init( \ <<'END_HERE');
               log4perl.logger = WARN, A1
               log4perl.appender.A1 = Log::Log4perl::Appender::Screen
               log4perl.appender.A1.layout = \
                   Log::Log4perl::Layout::PatternLayout
               log4perl.appender.A1.layout.ConversionPattern = %m%n
           END_HERE

           # *** RIGHT! ***

       (note the single quotes around 'END_HERE') or use "q{...}" instead of a here-document and
       Perl will treat the backslashes at line-end as intended.

   I want to suppress certain messages based on their content!
       Let's assume you've plastered all your functions with Log4perl statements like

           sub some_func {

               INFO("Begin of function");

               # ... Stuff happens here ...

               INFO("End of function");
           }

       to issue two log messages, one at the beginning and one at the end of each function. Now
       you want to suppress the message at the beginning and only keep the one at the end, what
       can you do? You can't use the category mechanism, because both messages are issued from
       the same package.

       Log::Log4perl's custom filters (0.30 or better) provide an interface for the Log4perl user
       to step in right before a message gets logged and decide if it should be written out or
       suppressed, based on the message content or other parameters:

           use Log::Log4perl qw(:easy);

           Log::Log4perl::init( \ <<'EOT' );
               log4perl.logger             = INFO, A1
               log4perl.appender.A1        = Log::Log4perl::Appender::Screen
               log4perl.appender.A1.layout = \
                   Log::Log4perl::Layout::PatternLayout
               log4perl.appender.A1.layout.ConversionPattern = %m%n

               log4perl.filter.M1 = Log::Log4perl::Filter::StringMatch
               log4perl.filter.M1.StringToMatch = Begin
               log4perl.filter.M1.AcceptOnMatch = false

               log4perl.appender.A1.Filter = M1
       EOT

       The last four statements in the configuration above are defining a custom filter "M1" of
       type "Log::Log4perl::Filter::StringMatch", which comes with Log4perl right out of the box
       and allows you to define a text pattern to match (as a perl regular expression) and a flag
       "AcceptOnMatch" indicating if a match is supposed to suppress the message or let it pass
       through.

       The last line then assigns this filter to the "A1" appender, which will call it every time
       it receives a message to be logged and throw all messages out not matching the regular
       expression "Begin".

       Instead of using the standard "Log::Log4perl::Filter::StringMatch" filter, you can define
       your own, simply using a perl subroutine:

           log4perl.filter.ExcludeBegin  = sub { !/Begin/ }
           log4perl.appender.A1.Filter   = ExcludeBegin

       For details on custom filters, check Log::Log4perl::Filter.

   My new module uses Log4perl -- but what happens if the calling program didn't configure it?
       If a Perl module uses Log::Log4perl, it will typically rely on the calling program to
       initialize it. If it is using Log::Log4perl in ":easy" mode, like in

           package MyMod;
           use Log::Log4perl qw(:easy);

           sub foo {
               DEBUG("In foo");
           }

           1;

       and the calling program doesn't initialize Log::Log4perl at all (e.g. because it has no
       clue that it's available), Log::Log4perl will silently ignore all logging messages.
       However, if the module is using Log::Log4perl in regular mode like in

           package MyMod;
           use Log::Log4perl qw(get_logger);

           sub foo {
               my $logger = get_logger("");
               $logger->debug("blah");
           }

           1;

       and the main program is just using the module like in

           use MyMode;
           MyMode::foo();

       then Log::Log4perl will also ignore all logging messages but issue a warning like

           Log4perl: Seems like no initialization happened.
           Forgot to call init()?

       (only once!) to remind novice users to not forget to initialize the logging system before
       using it.  However, if you want to suppress this message, just add the ":nowarn" target to
       the module's "use Log::Log4perl" call:

           use Log::Log4perl qw(get_logger :nowarn);

       This will have Log::Log4perl silently ignore all logging statements if no initialization
       has taken place. If, instead of using init(), you're using Log4perl's API to define
       loggers and appenders, the same notification happens if no call to add_appenders() is
       made, i.e. no appenders are defined.

       If the module wants to figure out if some other program part has already initialized
       Log::Log4perl, it can do so by calling

           Log::Log4perl::initialized()

       which will return a true value in case Log::Log4perl has been initialized and a false
       value if not.

   How can I synchronize access to an appender?
       If you're using the same instance of an appender in multiple processes, and each process
       is passing on messages to the appender in parallel, you might end up with overlapping log
       entries.

       Typical scenarios include a file appender that you create in the main program, and which
       will then be shared between the parent and a forked child process. Or two separate
       processes, each initializing a Log4perl file appender on the same logfile.

       Log::Log4perl won't synchronize access to the shared logfile by default. Depending on your
       operating system's flush mechanism, buffer size and the size of your messages, there's a
       small chance of an overlap.

       The easiest way to prevent overlapping messages in logfiles written to by multiple
       processes is setting the file appender's "syswrite" flag along with a file write mode of
       "append".  This makes sure that "Log::Log4perl::Appender::File" uses "syswrite()" (which
       is guaranteed to run uninterrupted) instead of "print()" which might buffer the message or
       get interrupted by the OS while it is writing. And in "append" mode, the OS kernel ensures
       that multiple processes share one end-of-file marker, ensuring that each process writes to
       the real end of the file. (The value of "append" for the "mode" parameter is the default
       setting in Log4perl's file appender so you don't have to set it explicitely.)

             # Guarantees atomic writes

           log4perl.category.Bar.Twix          = WARN, Logfile

           log4perl.appender.Logfile           = Log::Log4perl::Appender::File
           log4perl.appender.Logfile.mode      = append
           log4perl.appender.Logfile.syswrite  = 1
           log4perl.appender.Logfile.filename  = test.log
           log4perl.appender.Logfile.layout    = SimpleLayout

       Another guaranteed way of having messages separated with any kind of appender is putting a
       Log::Log4perl::Appender::Synchronized composite appender in between Log::Log4perl and the
       real appender. It will make sure to let messages pass through this virtual gate one by one
       only.

       Here's a sample configuration to synchronize access to a file appender:

           log4perl.category.Bar.Twix          = WARN, Syncer

           log4perl.appender.Logfile           = Log::Log4perl::Appender::File
           log4perl.appender.Logfile.autoflush = 1
           log4perl.appender.Logfile.filename  = test.log
           log4perl.appender.Logfile.layout    = SimpleLayout

           log4perl.appender.Syncer            = Log::Log4perl::Appender::Synchronized
           log4perl.appender.Syncer.appender   = Logfile

       "Log::Log4perl::Appender::Synchronized" uses the "IPC::Shareable" module and its
       semaphores, which will slow down writing the log messages, but ensures sequential access
       featuring atomic checks.  Check Log::Log4perl::Appender::Synchronized for details.

   Can I use Log::Log4perl with log4j's Chainsaw?
       Yes, Log::Log4perl can be configured to send its events to log4j's graphical log UI
       Chainsaw.

       Here's how it works:

       o   Get Guido Carls' <gcarls AT cpan.org> Log::Log4perl extension
           "Log::Log4perl::Layout::XMLLayout" from CPAN and install it:

               perl -MCPAN -eshell
               cpan> install Log::Log4perl::Layout::XMLLayout

       o   Install and start Chainsaw, which is part of the "log4j" distribution now (see
           http://jakarta.apache.org/log4j ). Create a configuration file like

             <log4j:configuration debug="true">
               <plugin name="XMLSocketReceiver"
                       class="org.apache.log4j.net.XMLSocketReceiver">
                 <param name="decoder" value="org.apache.log4j.xml.XMLDecoder"/>
                 <param name="Port" value="4445"/>
               </plugin>
               <root> <level value="debug"/> </root>
             </log4j:configuration>

           and name it e.g. "config.xml". Then start Chainsaw like

             java -Dlog4j.debug=true -Dlog4j.configuration=config.xml \
               -classpath ".:log4j-1.3alpha.jar:log4j-chainsaw-1.3alpha.jar" \
               org.apache.log4j.chainsaw.LogUI

           and watch the GUI coming up.

       o   Configure Log::Log4perl to use a socket appender with an XMLLayout, pointing to the
           host/port where Chainsaw (as configured above) is waiting with its XMLSocketReceiver:

             use Log::Log4perl qw(get_logger);
             use Log::Log4perl::Layout::XMLLayout;

             my $conf = q(
               log4perl.category.Bar.Twix          = WARN, Appender
               log4perl.appender.Appender          = Log::Log4perl::Appender::Socket
               log4perl.appender.Appender.PeerAddr = localhost
               log4perl.appender.Appender.PeerPort = 4445
               log4perl.appender.Appender.layout   = Log::Log4perl::Layout::XMLLayout
             );

             Log::Log4perl::init(\$conf);

               # Nasty hack to suppress encoding header
             my $app = Log::Log4perl::appenders->{"Appender"};
             $app->layout()->{enc_set} = 1;

             my $logger = get_logger("Bar.Twix");
             $logger->error("One");

           The nasty hack shown in the code snippet above is currently (October 2003) necessary,
           because Chainsaw expects XML messages to arrive in a format like

             <log4j:event logger="Bar.Twix"
                          timestamp="1066794904310"
                          level="ERROR"
                          thread="10567">
               <log4j:message><![CDATA[Two]]></log4j:message>
               <log4j:NDC><![CDATA[undef]]></log4j:NDC>
               <log4j:locationInfo class="main"
                 method="main"
                 file="./t"
                 line="32">
               </log4j:locationInfo>
             </log4j:event>

           without a preceding

             <?xml version = "1.0" encoding = "iso8859-1"?>

           which Log::Log4perl::Layout::XMLLayout applies to the first event sent over the
           socket.

       See figure 1 for a screenshot of Chainsaw in action, receiving events from the Perl script
       shown above.

       Many thanks to Chainsaw's Scott Deboy <sdeboy AT comotivsystems.com> for his support!

   How can I run Log::Log4perl under mod_perl?
       In persistent environments it's important to play by the rules outlined in section
       "Initialize once and only once" in Log::Log4perl.  If you haven't read this yet, please go
       ahead and read it right now. It's very important.

       And no matter if you use a startup handler to init() Log::Log4perl or use the init_once()
       strategy (added in 0.42), either way you're very likely to have unsynchronized writes to
       logfiles.

       If Log::Log4perl is configured with a log file appender, and it is initialized via the
       Apache startup handler, the file handle created initially will be shared among all Apache
       processes. Similarly, with the init_once() approach: although every process has a separate
       L4p configuration, processes are gonna share the appender file names instead, effectively
       opening several different file handles on the same file.

       Now, having several appenders using the same file handle or having several appenders
       logging to the same file unsynchronized, this might result in overlapping messages.
       Sometimes, this is acceptable. If it's not, here's two strategies:

       o   Use the Log::Log4perl::Appender::Synchronized appender to connect to your file
           appenders. Here's the writeup:
           http://log4perl.sourceforge.net/releases/Log-Log4perl/docs/html/Log/Log4perl/FAQ.html#23804

       o   Use a different logfile for every process like in

                #log4perl.conf
                ...
                log4perl.appender.A1.filename = sub { "mylog.$$.log" }

   My program already uses warn() and die(). How can I switch to Log4perl?
       If your program already uses Perl's "warn()" function to spew out error messages and you'd
       like to channel those into the Log4perl world, just define a "__WARN__" handler where your
       program or module resides:

           use Log::Log4perl qw(:easy);

           $SIG{__WARN__} = sub {
               local $Log::Log4perl::caller_depth =
                   $Log::Log4perl::caller_depth + 1;
               WARN @_;
           };

       Why the "local" setting of $Log::Log4perl::caller_depth?  If you leave that out,
       "PatternLayout" conversion specifiers like %M or %F (printing the current function/method
       and source filename) will refer to where the __WARN__ handler resides, not the environment
       Perl's "warn()" function was issued from. Increasing "caller_depth" adjusts for this
       offset. Having it "local", makes sure the level gets set back after the handler exits.

       Once done, if your program does something like

           sub some_func {
               warn "Here's a warning";
           }

       you'll get (depending on your Log::Log4perl configuration) something like

           2004/02/19 20:41:02-main::some_func: Here's a warning at ./t line 25.

       in the appropriate appender instead of having a screen full of STDERR messages. It also
       works with the "Carp" module and its "carp()" and "cluck()" functions.

       If, on the other hand, catching "die()" and friends is required, a "__DIE__" handler is
       appropriate:

           $SIG{__DIE__} = sub {
               if($^S) {
                   # We're in an eval {} and don't want log
                   # this message but catch it later
                   return;
               }
               local $Log::Log4perl::caller_depth =
                   $Log::Log4perl::caller_depth + 1;
               LOGDIE @_;
           };

       This will call Log4perl's "LOGDIE()" function, which will log a fatal error and then call
       die() internally, causing the program to exit. Works equally well with "Carp"'s "croak()"
       and "confess()" functions.

   Some module prints messages to STDERR. How can I funnel them to Log::Log4perl?
       If a module you're using doesn't use Log::Log4perl but prints logging messages to STDERR
       instead, like

           ########################################
           package IgnorantModule;
           ########################################

           sub some_method {
               print STDERR "Parbleu! An error!\n";
           }

           1;

       there's still a way to capture these messages and funnel them into Log::Log4perl, even
       without touching the module. What you need is a trapper module like

           ########################################
           package Trapper;
           ########################################

           use Log::Log4perl qw(:easy);

           sub TIEHANDLE {
               my $class = shift;
               bless [], $class;
           }

           sub PRINT {
               my $self = shift;
               $Log::Log4perl::caller_depth++;
               DEBUG @_;
               $Log::Log4perl::caller_depth--;
           }

           1;

       and a "tie" command in the main program to tie STDERR to the trapper module along with
       regular Log::Log4perl initialization:

           ########################################
           package main;
           ########################################

           use Log::Log4perl qw(:easy);

           Log::Log4perl->easy_init(
               {level  => $DEBUG,
                file   => 'stdout',   # make sure not to use stderr here!
                layout => "%d %M: %m%n",
               });

           tie *STDERR, "Trapper";

       Make sure not to use STDERR as Log::Log4perl's file appender here (which would be the
       default in ":easy" mode), because it would end up in an endless recursion.

       Now, calling

           IgnorantModule::some_method();

       will result in the desired output

           2004/05/06 11:13:04 IgnorantModule::some_method: Parbleu! An error!

   How come PAR (Perl Archive Toolkit) creates executables which then can't find their
       Log::Log4perl appenders?
       If not instructed otherwise, "Log::Log4perl" dynamically pulls in appender classes found
       in its configuration. If you specify

           #!/usr/bin/perl
           # mytest.pl

           use Log::Log4perl qw(get_logger);

           my $conf = q(
             log4perl.category.Bar.Twix = WARN, Logfile
             log4perl.appender.Logfile  = Log::Log4perl::Appender::Screen
             log4perl.appender.Logfile.layout = SimpleLayout
           );

           Log::Log4perl::init(\$conf);
           my $logger = get_logger("Bar::Twix");
           $logger->error("Blah");

       then "Log::Log4perl::Appender::Screen" will be pulled in while the program runs, not at
       compile time. If you have PAR compile the script above to an executable binary via

           pp -o mytest mytest.pl

       and then run "mytest" on a machine without having Log::Log4perl installed, you'll get an
       error message like

           ERROR: can't load appenderclass 'Log::Log4perl::Appender::Screen'
           Can't locate Log/Log4perl/Appender/Screen.pm in @INC ...

       Why? At compile time, "pp" didn't realize that "Log::Log4perl::Appender::Screen" would be
       needed later on and didn't wrap it into the executable created. To avoid this, either say
       "use Log::Log4perl::Appender::Screen" in the script explicitely or compile it with

           pp -o mytest -M Log::Log4perl::Appender::Screen mytest.pl

       to make sure the appender class gets included.

   How can I access a custom appender defined in the configuration?
       Any appender defined in the configuration file or somewhere in the code can be accessed
       later via "Log::Log4perl->appender_by_name("appender_name")", which returns a reference
       the the appender object.

       Once you've got a hold of the object, it can be queried or modified to your liking. For
       example, see the custom "IndentAppender" defined below: After calling "init()" to define
       the Log4perl settings, the appender object is retrieved to call its "indent_more()" and
       "indent_less()" methods to control indentation of messages:

           package IndentAppender;

           sub new {
               bless { indent => 0 }, $_[0];
           }

           sub indent_more  { $_[0]->{indent}++ }
           sub indent_less  { $_[0]->{indent}-- }

           sub log {
               my($self, %params) = @_;
               print " " x $self->{indent}, $params{message};
           }

           package main;

           use Log::Log4perl qw(:easy);

           my $conf = q(
           log4perl.category          = DEBUG, Indented
           log4perl.appender.Indented = IndentAppender
           log4perl.appender.Indented.layout = Log::Log4perl::Layout::SimpleLayout
           );

           Log::Log4perl::init(\$conf);

           my $appender = Log::Log4perl->appender_by_name("Indented");

           DEBUG "No identation";
           $appender->indent_more();
           DEBUG "One more";
           $appender->indent_more();
           DEBUG "Two more";
           $appender->indent_less();
           DEBUG "One less";

       As you would expect, this will print

           DEBUG - No identation
            DEBUG - One more
             DEBUG - Two more
            DEBUG - One less

       because the very appender used by Log4perl is modified dynamically at runtime.

   I don't know if Log::Log4perl is installed. How can I prepare my script?
       In case your script needs to be prepared for environments that may or may not have
       Log::Log4perl installed, there's a trick.

       If you put the following BEGIN blocks at the top of the program, you'll be able to use the
       DEBUG(), INFO(), etc. macros in Log::Log4perl's ":easy" mode.  If Log::Log4perl is
       installed in the target environment, the regular Log::Log4perl rules apply. If not, all of
       DEBUG(), INFO(), etc. are "stubbed" out, i.e. they turn into no-ops:

           use warnings;
           use strict;

           BEGIN {
               eval { require Log::Log4perl; };

               if($@) {
                   print "Log::Log4perl not installed - stubbing.\n";
                   no strict qw(refs);
                   *{"main::$_"} = sub { } for qw(DEBUG INFO WARN ERROR FATAL);
               } else {
                   no warnings;
                   print "Log::Log4perl installed - life is good.\n";
                   require Log::Log4perl::Level;
                   Log::Log4perl::Level->import(__PACKAGE__);
                   Log::Log4perl->import(qw(:easy));
                   Log::Log4perl->easy_init($main::DEBUG);
               }
           }

               # The regular script begins ...
           DEBUG "Hey now!";

       This snippet will first probe for Log::Log4perl, and if it can't be found, it will alias
       DEBUG(), INFO(), with empty subroutines via typeglobs.  If Log::Log4perl is available, its
       level constants are first imported ($DEBUG, $INFO, etc.) and then "easy_init()" gets
       called to initialize the logging system.

   Can file appenders create files with different permissions?
       Typically, when "Log::Log4perl::Appender::File" creates a new file, its permissions are
       set to "rw-r--r--". Why? Because your environment's umask most likely defaults to 0022,
       that's the standard setting.

       What's a umask, you're asking? It's a template that's applied to the permissions of all
       newly created files. While calls like "open(FILE, ">foo")" will always try to create files
       in "rw-rw-rw- " mode, the system will apply the current umask template to determine the
       final permission setting. umask is a bit mask that's inverted and then applied to the
       requested permission setting, using a bitwise AND:

           $request_permission &~ $umask

       So, a umask setting of 0000 (the leading 0 simply indicates an octal value) will create
       files in "rw-rw-rw-" mode, a setting of 0277 will use "r--------", and the standard 0022
       will use "rw-r--r--".

       As an example, if you want your log files to be created with "rw-r--rw-" permissions, use
       a umask of 0020 before calling Log::Log4perl->init():

           use Log::Log4perl;

           umask 0020;
               # Creates log.out in rw-r--rw mode
           Log::Log4perl->init(\ q{
               log4perl.logger = WARN, File
               log4perl.appender.File = Log::Log4perl::Appender::File
               log4perl.appender.File.filename = log.out
               log4perl.appender.File.layout = SimpleLayout
           });

   Using Log4perl in an END block causes a problem!
       It's not easy to get to this error, but if you write something like

           END { Log::Log4perl::get_logger()->debug("Hey there."); }

           use Log::Log4perl qw(:easy);
           Log::Log4perl->easy_init($DEBUG);

       it won't work. The reason is that "Log::Log4perl" defines an END block that cleans up all
       loggers. And perl will run END blocks in the reverse order as they're encountered in the
       compile phase, so in the scenario above, the END block will run after Log4perl has cleaned
       up its loggers.

       Placing END blocks using Log4perl after a "use Log::Log4perl" statement fixes the problem:

           use Log::Log4perl qw(:easy);
           Log::Log4perl->easy_init($DEBUG);

           END { Log::Log4perl::get_logger()->debug("Hey there."); }

       In this scenario, the shown END block is executed before Log4perl cleans up and the debug
       message will be processed properly.

   Help! My appender is throwing a "Wide character in print" warning!
       This warning shows up when Unicode strings are printed without precautions. The warning
       goes away if the complaining appender is set to utf-8 mode:

             # Either in the log4perl configuration file:
         log4perl.appender.Logfile.filename = test.log
         log4perl.appender.Logfile.utf8     = 1

             # Or, in easy mode:
         Log::Log4perl->easy_init( {
           level => $DEBUG,
           file  => ":utf8> test.log"
         } );

       If the complaining appender is a screen appender, set its "utf8" option:

             log4perl.appender.Screen.stderr = 1
             log4perl.appender.Screen.utf8   = 1

       Alternatively, "binmode" does the trick:

             # Either STDOUT ...
           binmode(STDOUT, ":utf8);

             # ... or STDERR.
           binmode(STDERR, ":utf8);

       Some background on this: Perl's strings are either byte strings or Unicode strings. "Mike"
       is a byte string.  "\x{30DE}\x{30A4}\x{30AF}" is a Unicode string. Unicode strings are
       marked specially and are UTF-8 encoded internally.

       If you print a byte string to STDOUT, all is well, because STDOUT is by default set to
       byte mode. However, if you print a Unicode string to STDOUT without precautions, "perl"
       will try to transform the Unicode string back to a byte string before printing it out.
       This is troublesome if the Unicode string contains 'wide' characters which can't be
       represented in Latin-1.

       For example, if you create a Unicode string with three japanese Katakana characters as in

           perl -le 'print "\x{30DE}\x{30A4}\x{30AF}"'

       (coincidentally pronounced Ma-i-ku, the japanese pronounciation of "Mike"), STDOUT is in
       byte mode and the warning

           Wide character in print at ./script.pl line 14.

       appears. Setting STDOUT to UTF-8 mode as in

           perl -le 'binmode(STDOUT, ":utf8"); print "\x{30DE}\x{30A4}\x{30AF}"'

       will silently print the Unicode string to STDOUT in UTF-8. To see the characters printed,
       you'll need a UTF-8 terminal with a font including japanese Katakana characters.

   How can I send errors to the screen, and debug messages to a file?
       Let's assume you want to maintain a detailed DEBUG output in a file and only messages of
       level ERROR and higher should be printed on the screen. Often times, developers come up
       with something like this:

            # Wrong!!!
           log4perl.logger = DEBUG, FileApp
           log4perl.logger = ERROR, ScreenApp
            # Wrong!!!

       This won't work, however. Logger definitions aren't additive, and the second statement
       will overwrite the first one. Log4perl versions below 1.04 were silently accepting this,
       leaving people confused why it wouldn't work as expected.  As of 1.04, this will throw a
       fatal error to notify the user of the problem.

       What you want to do instead, is this:

           log4perl.logger                    = DEBUG, FileApp, ScreenApp

           log4perl.appender.FileApp          = Log::Log4perl::Appender::File
           log4perl.appender.FileApp.filename = test.log
           log4perl.appender.FileApp.layout   = SimpleLayout

           log4perl.appender.ScreenApp          = Log::Log4perl::Appender::Screen
           log4perl.appender.ScreenApp.stderr   = 0
           log4perl.appender.ScreenApp.layout   = SimpleLayout
              ### limiting output to ERROR messages
           log4perl.appender.ScreenApp.Threshold = ERROR
              ###

       Note that without the second appender's "Threshold" setting, both appenders would receive
       all messages prioritized DEBUG and higher. With the threshold set to ERROR, the second
       appender will filter the messages as required.

   Where should I put my logfiles?
       Your log files may go anywhere you want them, but the effective user id of the calling
       process must have write access.

       If the log file doesn't exist at program start, Log4perl's file appender will create it.
       For this, it needs write access to the directory where the new file will be located in. If
       the log file already exists at startup, the process simply needs write access to the file.
       Note that it will need write access to the file's directory if you're encountering
       situations where the logfile gets recreated, e.g. during log rotation.

       If Log::Log4perl is used by a web server application (e.g. in a CGI script or mod_perl),
       then the webserver's user (usually "nobody" or "www") must have the permissions mentioned
       above.

       To prepare your web server to use log4perl, we'd recommend:

           webserver:~$ su -
           webserver:~# mkdir /var/log/cgiapps
           webserver:~# chown nobody:root /var/log/cgiapps/
           webserver:~# chown nobody:root -R /var/log/cgiapps/
           webserver:~# chmod 02755 -R /var/log/cgiapps/

       Then set your /etc/log4perl.conf file to include:

           log4perl.appender.FileAppndr1.filename =
               /var/log/cgiapps/<app-name>.log

   How can my file appender deal with disappearing log files?
       The file appender that comes with Log4perl, Log::Log4perl::Appender::File, will open a
       specified log file at initialization time and will keep writing to it via a file handle.

       In case the associated file goes way, messages written by a long-running process will
       still be written to the file handle. In case the file has been moved to a different
       location on the same file system, the writer will keep writing to it under the new
       filename. In case the file has been removed from the file system, the log messages will
       end up in nowhere land. This is not a bug in Log4perl, this is how Unix works. There is no
       error message in this case, because the writer has no idea that the file handle is not
       associated with a visible file.

       To prevent the loss of log messages when log files disappear, the file appender's
       "recreate" option needs to be set to a true value:

           log4perl.appender.Logfile.recreate = 1

       This will instruct the file appender to check in regular intervals (default: 30 seconds)
       if the log file is still there. If it finds out that the file is missing, it will recreate
       it.

       Continuously checking if the log file still exists is fairly expensive. For this reason it
       is only performed every 30 seconds. To change this interval, the option
       "recreate_check_interval" can be set to the number of seconds between checks. In the
       extreme case where the check should be performed before every write, it can even be set to
       0:

           log4perl.appender.Logfile.recreate = 1
           log4perl.appender.Logfile.recreate_check_interval = 0

       To avoid having to check the file system so frequently, a signal handler can be set up:

           log4perl.appender.Logfile.recreate = 1
           log4perl.appender.Logfile.recreate_check_signal = USR1

       This will install a signal handler which will recreate a missing log file immediatly when
       it receives the defined signal.

       Note that the init_and_watch() method for Log4perl's initialization can also be instructed
       to install a signal handler, usually using the HUP signal. Make sure to use a different
       signal if you're using both of them at the same time.

   How can I rotate a logfile with newsyslog?
       Here's a few things that need to be taken care of when using the popular log file rotating
       utilty "newsyslog" (http://www.courtesan.com/newsyslog) with Log4perl's file appender in
       long-running processes.

       For example, with a newsyslog configuration like

           # newsyslog.conf
           /tmp/test.log 666  12  5  *  B

       and a call to

           # newsyslog -f /path/to/newsyslog.conf

       "newsyslog" will take action if "/tmp/test.log" is larger than the specified 5K in size.
       It will move the current log file "/tmp/test.log" to "/tmp/test.log.0" and create a new
       and empty "/tmp/test.log" with the specified permissions (this is why "newsyslog" needs to
       run as root).  An already existing "/tmp/test.log.0" would be moved to "/tmp/test.log.1",
       "/tmp/test.log.1" to "/tmp/test.log.2", and so forth, for every one of a max number of 12
       archived logfiles that have been configured in "newsyslog.conf".

       Although a new file has been created, from Log4perl's appender's point of view, this
       situation is identical to the one described in the previous FAQ entry, labeled "How can my
       file appender deal with disappearing log files".

       To make sure that log messages are written to the new log file and not to an archived one
       or end up in nowhere land, the appender's "recreate" and "recreate_check_interval" have to
       be configured to deal with the 'disappearing' log file.

       The situation gets interesting when "newsyslog"'s option to compress archived log files is
       enabled. This causes the original log file not to be moved, but to disappear. If the file
       appender isn't configured to recreate the logfile in this situation, log messages will
       actually be lost without warning. This also applies for the short time frame of
       "recreate_check_interval" seconds in between the recreator's file checks.

       To make sure that no messages get lost, one option is to set the interval to

           log4perl.appender.Logfile.recreate_check_interval = 0

       However, this is fairly expensive. A better approach is to define a signal handler:

           log4perl.appender.Logfile.recreate = 1
           log4perl.appender.Logfile.recreate_check_signal  = USR1
           log4perl.appender.Logfile.recreate_pid_write = /tmp/myappid

       As a service for "newsyslog" users, Log4perl's file appender writes the current process ID
       to a PID file specified by the "recreate_pid_write" option.  "newsyslog" then needs to be
       configured as in

           # newsyslog.conf configuration for compressing archive files and
           # sending a signal to the Log4perl-enabled application
           /tmp/test.log 666  12  5  *  B /tmp/myappid 30

       to send the defined signal (30, which is USR1 on FreeBSD) to the application process at
       rotation time. Note that the signal number is different on Linux, where USR1 denotes as
       10. Check "man signal" for details.

   How can a process under user id A log to a file under user id B?
       This scenario often occurs in configurations where processes run under various user IDs
       but need to write to a log file under a fixed, but different user id.

       With a traditional file appender, the log file will probably be created under one user's
       id and appended to under a different user's id. With a typical umask of 0002, the file
       will be created with -rw-rw-r-- permissions. If a user who's not in the first user's group
       subsequently appends to the log file, it will fail because of a permission problem.

       Two potential solutions come to mind:

       o   Creating the file with a umask of 0000 will allow all users to append to the log file.
           Log4perl's file appender "Log::Log4perl::Appender::File" has an "umask" option that
           can be set to support this:

               log4perl.appender.File = Log::Log4perl::Appender::File
               log4perl.appender.File.umask = sub { 0000 };

           This way, the log file will be created with -rw-rw-rw- permissions and therefore has
           world write permissions. This might open up the logfile for unwanted manipulations by
           arbitrary users, though.

       o   Running the process under an effective user id of "root" will allow it to write to the
           log file, no matter who started the process.  However, this is not a good idea,
           because of security concerns.

       Luckily, under Unix, there's the syslog daemon which runs as root and takes log requests
       from user processes over a socket and writes them to log files as configured in
       "/etc/syslog.conf".

       By modifying "/etc/syslog.conf" and HUPing the syslog daemon, you can configure new log
       files:

           # /etc/syslog.conf
           ...
           user.* /some/path/file.log

       Using the "Log::Dispatch::Syslog" appender, which comes with the "Log::Log4perl"
       distribution, you can then send messages via syslog:

           use Log::Log4perl qw(:easy);

           Log::Log4perl->init(\<<EOT);
               log4perl.logger = DEBUG, app
               log4perl.appender.app=Log::Dispatch::Syslog
               log4perl.appender.app.Facility=user
               log4perl.appender.app.layout=SimpleLayout
           EOT

               # Writes to /some/path/file.log
           ERROR "Message!";

       This way, the syslog daemon will solve the permission problem.

       Note that while it is possible to use syslog() without Log4perl (syslog supports log
       levels, too), traditional syslog setups have a significant drawback.

       Without Log4perl's ability to activate logging in only specific parts of a system, complex
       systems will trigger log events all over the place and slow down execution to a crawl at
       high debug levels.

       Remote-controlling logging in the hierarchical parts of an application via Log4perl's
       categories is one of its most distinguished features.  It allows for enabling high debug
       levels in specified areas without noticable performance impact.

   I want to use UTC instead of the local time!
       If a layout defines a date, Log::Log4perl uses local time to populate it.  If you want UTC
       instead, set

           $Log::Log4perl::DateFormat::GMTIME = 1;

       in your program before the first log statement.

   Can Log4perl intercept messages written to a filehandle?
       You have a function that prints to a filehandle. You want to tie into that filehandle and
       forward all arriving messages to a Log4perl logger.

       First, let's write a package that ties a file handle and forwards it to a Log4perl logger:

           package FileHandleLogger;
           use Log::Log4perl qw(:levels get_logger);

           sub TIEHANDLE {
              my($class, %options) = @_;

              my $self = {
                  level    => $DEBUG,
                  category => '',
                  %options
              };

              $self->{logger} = get_logger($self->{category}),
              bless $self, $class;
           }

           sub PRINT {
               my($self, @rest) = @_;
               $Log::Log4perl::caller_depth++;
               $self->{logger}->log($self->{level}, @rest);
               $Log::Log4perl::caller_depth--;
           }

           sub PRINTF {
               my($self, $fmt, @rest) = @_;
               $Log::Log4perl::caller_depth++;
               $self->PRINT(sprintf($fmt, @rest));
               $Log::Log4perl::caller_depth--;
           }

           1;

       Now, if you have a function like

           sub function_printing_to_fh {
               my($fh) = @_;
               printf $fh "Hi there!\n";
           }

       which takes a filehandle and prints something to it, it can be used with Log4perl:

           use Log::Log4perl qw(:easy);
           usa FileHandleLogger;

           Log::Log4perl->easy_init($DEBUG);

           tie *SOMEHANDLE, 'FileHandleLogger' or
               die "tie failed ($!)";

           function_printing_to_fh(*SOMEHANDLE);
               # prints "2007/03/22 21:43:30 Hi there!"

       If you want, you can even specify a different log level or category:

           tie *SOMEHANDLE, 'FileHandleLogger',
               level => $INFO, category => "Foo::Bar" or die "tie failed ($!)";

   I want multiline messages rendered line-by-line!
       With the standard "PatternLayout", if you send a multiline message to an appender as in

           use Log::Log4perl qw(:easy);
           Log

       it gets rendered this way:

           2007/04/04 23:23:39 multi
           line
           message

       If you want each line to be rendered separately according to the layout use
       "Log::Log4perl::Layout::PatternLayout::Multiline":

           use Log::Log4perl qw(:easy);

           Log::Log4perl->init(\<<EOT);
             log4perl.category         = DEBUG, Screen
             log4perl.appender.Screen = Log::Log4perl::Appender::Screen
             log4perl.appender.Screen.layout = \\
               Log::Log4perl::Layout::PatternLayout::Multiline
             log4perl.appender.Screen.layout.ConversionPattern = %d %m %n
           EOT

           DEBUG "some\nmultiline\nmessage";

       and you'll get

           2007/04/04 23:23:39 some
           2007/04/04 23:23:39 multiline
           2007/04/04 23:23:39 message

       instead.

   I'm on Windows and I'm getting all these 'redefined' messages!
       If you're on Windows and are getting warning messages like

         Constant subroutine Log::Log4perl::_INTERNAL_DEBUG redefined at
           C:/Programme/Perl/lib/constant.pm line 103.
         Subroutine import redefined at
           C:/Programme/Perl/site/lib/Log/Log4Perl.pm line 69.
         Subroutine initialized redefined at
           C:/Programme/Perl/site/lib/Log/Log4Perl.pm line 207.

       then chances are that you're using 'Log::Log4Perl' (wrong uppercase P) instead of the
       correct 'Log::Log4perl'. Perl on Windows doesn't handle this error well and spits out a
       slew of confusing warning messages. But now you know, just use the correct module name and
       you'll be fine.

   Log4perl complains that no initialization happened during shutdown!
       If you're using Log4perl log commands in DESTROY methods of your objects, you might see
       confusing messages like

           Log4perl: Seems like no initialization happened. Forgot to call init()?
           Use of uninitialized value in subroutine entry at
           /home/y/lib/perl5/site_perl/5.6.1/Log/Log4perl.pm line 134 during global
           destruction. (in cleanup) Undefined subroutine &main:: called at
           /home/y/lib/perl5/site_perl/5.6.1/Log/Log4perl.pm line 134 during global
           destruction.

       when the program shuts down. What's going on?

       This phenomenon happens if you have circular references in your objects, which perl can't
       clean up when an object goes out of scope but waits until global destruction instead. At
       this time, however, Log4perl has already shut down, so you can't use it anymore.

       For example, here's a simple class which uses a logger in its DESTROY method:

           package A;
           use Log::Log4perl qw(:easy);
           sub new { bless {}, shift }
           sub DESTROY { DEBUG "Waaah!"; }

       Now, if the main program creates a self-referencing object, like in

           package main;
           use Log::Log4perl qw(:easy);
           Log::Log4perl->easy_init($DEBUG);

           my $a = A->new();
           $a->{selfref} = $a;

       then you'll see the error message shown above during global destruction.  How to tackle
       this problem?

       First, you should clean up your circular references before global destruction. They will
       not only cause objects to be destroyed in an order that's hard to predict, but also eat up
       memory until the program shuts down.

       So, the program above could easily be fixed by putting

           $a->{selfref} = undef;

       at the end or in an END handler. If that's hard to do, use weak references:

           package main;
           use Scalar::Util qw(weaken);
           use Log::Log4perl qw(:easy);
           Log::Log4perl->easy_init($DEBUG);

           my $a = A->new();
           $a->{selfref} = weaken $a;

       This allows perl to clean up the circular reference when the object goes out of scope, and
       doesn't wait until global destruction.

   How can I access POE heap values from Log4perl's layout?
       POE is a framework for creating multitasked applications running in a single process and a
       single thread. POE's threads equivalents are 'sessions' and since they run quasi-
       simultaneously, you can't use Log4perl's global NDC/MDC to hold session-specific data.

       However, POE already maintains a data store for every session. It is called 'heap' and is
       just a hash storing session-specific data in key-value pairs.  To access this per-session
       heap data from a Log4perl layout, define a custom cspec and reference it with the newly
       defined pattern in the layout:

           use strict;
           use POE;
           use Log::Log4perl qw(:easy);

           Log::Log4perl->init( \ q{
               log4perl.logger = DEBUG, Screen
               log4perl.appender.Screen = Log::Log4perl::Appender::Screen
               log4perl.appender.Screen.layout = PatternLayout
               log4perl.appender.Screen.layout.ConversionPattern = %U %m%n
               log4perl.PatternLayout.cspec.U = \
                   sub { POE::Kernel->get_active_session->get_heap()->{ user } }
           } );

           for (qw( Huey Lewey Dewey )) {
               POE::Session->create(
                   inline_states => {
                       _start    => sub {
                           $_[HEAP]->{user} = $_;
                           POE::Kernel->yield('hello');
                       },
                       hello     => sub {
                           DEBUG "I'm here now";
                       }
                   }
               );
           }

           POE::Kernel->run();
           exit;

       The code snippet above defines a new layout placeholder (called 'cspec' in Log4perl) %U
       which calls a subroutine, retrieves the active session, gets its heap and looks up the
       entry specified ('user').

       Starting with Log::Log4perl 1.20, cspecs also support parameters in curly braces, so you
       can say

           log4perl.appender.Screen.layout.ConversionPattern = %U{user} %U{id} %m%n
           log4perl.PatternLayout.cspec.U = \
                   sub { POE::Kernel->get_active_session-> \
                         get_heap()->{ $_[0]->{curlies} } }

       and print the POE session heap entries 'user' and 'id' with every logged message. For more
       details on cpecs, read the PatternLayout manual.

   I want to print something unconditionally!
       Sometimes it's a script that's supposed to log messages regardless if Log4perl has been
       initialized or not. Or there's a logging statement that's not going to be suppressed under
       any circumstances -- many people want to have the final word, make the executive decision,
       because it seems like the only logical choice.

       But think about it: First off, if a messages is supposed to be printed, where is it
       supposed to end up at? STDOUT? STDERR? And are you sure you want to set in stone that this
       message needs to be printed, while someone else might find it annoying and wants to get
       rid of it?

       The truth is, there's always going to be someone who wants to log a messages at all cost,
       but also another person who wants to suppress it with equal vigilance. There's no good way
       to serve these two conflicting desires, someone will always want to win at the cost of
       leaving the other party dissappointed.

       So, the best Log4perl offers is the ALWAYS level for a message that even fires if the
       system log level is set to $OFF:

           use Log::Log4perl qw(:easy);

           Log::Log4perl->easy_init( $OFF );
           ALWAYS "This gets logged always. Well, almost always";

       The logger won't fire, though, if Log4perl hasn't been initialized or if someone defines a
       custom log hurdle that's higher than $OFF.

       Bottom line: Leave the setting of the logging level to the initial Perl script -- let
       their owners decided what they want, no matter how tempting it may be to decide it for
       them.

SEE ALSO
       Log::Log4perl

LICENSE
       Copyright 2002-2013 by Mike Schilli <m AT perlmeister.com> and Kevin Goess <cpan AT goess.org>.

       This library is free software; you can redistribute it and/or modify it under the same
       terms as Perl itself.

AUTHOR
       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 AT lists.net

       Authors (please contact them via the list above, not directly): Mike Schilli
       <m AT perlmeister.com>, Kevin Goess <cpan AT 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.



perl v5.16.3                                2013-07-26                                     FAQ(3)

Generated by $Id: phpMan.php,v 4.55 2007/09/05 04:42:51 chedong Exp $ Author: Che Dong
On Apache
Under GNU General Public License
2024-04-26 23:31 @18.216.233.58 CrawledBy Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
Valid XHTML 1.0!Valid CSS!