home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / Appender.pm < prev    next >
Encoding:
Perl POD Document  |  2003-03-14  |  15.5 KB  |  480 lines

  1. ##################################################
  2. package Log::Log4perl::Appender;
  3. ##################################################
  4.  
  5. use 5.006;
  6. use strict;
  7. use warnings;
  8.  
  9. use Log::Log4perl::Level;
  10. use Log::Log4perl::Config;
  11.  
  12. use constant DEBUG => 0;
  13.  
  14. our $unique_counter = 0;
  15.  
  16. ##################################################
  17. sub reset {
  18. ##################################################
  19.     $unique_counter = 0;
  20. }
  21.  
  22. ##################################################
  23. sub unique_name {
  24. ##################################################
  25.         # THREADS: Need to lock here to make it thread safe
  26.     $unique_counter++;
  27.     my $unique_name = sprintf("app%03d", $unique_counter);
  28.         # THREADS: Need to unlock here to make it thread safe
  29.     return $unique_name;
  30. }
  31.  
  32. ##################################################
  33. sub new {
  34. ##################################################
  35.     my($class, $appenderclass, %params) = @_;
  36.  
  37.         # Pull in the specified Log::Log4perl::Appender object
  38.     eval {
  39.         no strict 'refs';
  40.         # see 'perldoc -f require' for why two evals
  41.         eval "require $appenderclass"
  42.              unless ${$appenderclass.'::IS_LOADED'};  #for unit tests, 
  43.                                                       #see 004Config
  44.              ;
  45.         die $@ if $@;
  46.  
  47.            # Eval erroneously succeeds on unknown appender classes if
  48.            # the eval string just consists of valid perl code (e.g. an
  49.            # appended ';' in $appenderclass variable). Fail if we see
  50.            # anything in there that can't be class name.
  51.         die "" if $appenderclass =~ /[^:\w]/;
  52.     };
  53.  
  54.     $@ and die "ERROR: appenderclass '$appenderclass' doesn't exist\n$@";
  55.  
  56.     $params{name} = unique_name() unless exists $params{name};
  57.  
  58.     # If it's a Log::Dispatch::File appender, default to append 
  59.     # mode (Log::Dispatch::File defaults to 'clobber') -- consensus 9/2002
  60.     if ($appenderclass eq 'Log::Dispatch::File' &&
  61.         ! exists $params{mode}) {
  62.         $params{mode} = 'append';
  63.     }
  64.  
  65.     my $appender = $appenderclass->new(
  66.             # Set min_level to the lowest setting. *we* are 
  67.             # controlling this now, the appender should just
  68.             # log it with no questions asked.
  69.         min_level => 'debug',
  70.             # Set 'name' and other parameters
  71.         map { $_ => $params{$_} } keys %params,
  72.     );
  73.  
  74.     my $self = {
  75.                  appender => $appender,
  76.                  name     => $params{name},
  77.                  layout   => undef,
  78.                  level    => $DEBUG,
  79.                };
  80.  
  81.         #whether to collapse arrays, etc.
  82.     $self->{warp_message} = $params{warp_message};
  83.     if($self->{warp_message} and
  84.        my $cref = 
  85.        Log::Log4perl::Config::compile_if_perl($self->{warp_message})) {
  86.         $self->{warp_message} = $cref;
  87.     }
  88.     
  89.     bless $self, $class;
  90.  
  91.     return $self;
  92. }
  93.  
  94. ##################################################
  95. sub threshold { # Set/Get the appender threshold
  96. ##################################################
  97.     my ($self, $level) = @_;
  98.  
  99.     print "Setting threshold to $level\n" if DEBUG;
  100.  
  101.     if(defined $level) {
  102.         # Checking for \d makes for a faster regex(p)
  103.         $self->{level} = ($level =~ /^(\d+)$/) ? $level :
  104.             # Take advantage of &to_priority's error reporting
  105.             Log::Log4perl::Level::to_priority($level);
  106.     }
  107.  
  108.     return $self->{level};
  109. }
  110.  
  111. ##################################################
  112. sub log { # Relay this call to Log::Dispatch::Whatever
  113. ##################################################
  114.     my ($self, $p, $category, $level) = @_;
  115.  
  116.     # Check if the appender has a last-minute veto in form
  117.     # of an "appender threshold"
  118.     if($self->{level} > $
  119.                         Log::Log4perl::Level::PRIORITY{$level}) {
  120.         print "$self->{level} > $level, aborting\n" if DEBUG;
  121.         return undef;
  122.     }
  123.  
  124.     # Run against the (yes only one) customized filter (which in turn
  125.     # might call other filters via the Boolean filter) and check if its
  126.     # ok() method approves the message or blocks it.
  127.     if($self->{filter}) {
  128.         if($self->{filter}->ok(%$p,
  129.                                log4p_category => $category,
  130.                                log4p_level    => $level )) {
  131.             print "Filter $self->{filter}->{name} passes\n" if DEBUG;
  132.         } else {
  133.             print "Filter $self->{filter}->{name} blocks\n" if DEBUG;
  134.             return undef;
  135.         }
  136.     }
  137.  
  138.     $self->{layout} || $self->layout();  #set to default if not already
  139.                                          #can this be moved?
  140.  
  141.     #doing the rendering in here 'cause this is 
  142.     #where we keep the layout
  143.  
  144.         #not defined, the normal case
  145.     if (! defined $self->{warp_message} ){ 
  146.             #join any message elements
  147.         $p->{message} = 
  148.             join($Log::Log4perl::JOIN_MSG_ARRAY_CHAR, 
  149.                  @{$p->{message}} 
  150.                  );
  151.         
  152.         #defined but false, e.g. Appender::DBI
  153.     } elsif (! $self->{warp_message}) {
  154.         ;  #leave the message alone
  155.  
  156.     } elsif (ref($self->{warp_message}) eq "CODE") {
  157.         #defined and a subref
  158.         $p->{message} = 
  159.             [$self->{warp_message}->(@{$p->{message}})];
  160.     } else {
  161.         #defined and a function name?
  162.         no strict qw(refs);
  163.         $p->{message} = 
  164.             [$self->{warp_message}->(@{$p->{message}})];
  165.     }
  166.  
  167.     $p->{message} = $self->{layout}->render($p->{message}, 
  168.                                             $category,
  169.                                             $level,
  170.                                             3 + $Log::Log4perl::caller_depth,
  171.                                             );
  172.  
  173.     $self->{appender}->log(%$p, 
  174.                             #these are used by our Appender::DBI
  175.                             log4p_category => $category,
  176.                             log4p_level  => $level,);
  177.     return 1;
  178. }
  179.  
  180. ##################################################
  181. sub name { # Set/Get the name
  182. ##################################################
  183.     my($self, $name) = @_;
  184.  
  185.         # Somebody wants to *set* the name?
  186.     if($name) {
  187.         $self->{name} = $name;
  188.     }
  189.  
  190.     return $self->{name};
  191. }
  192.  
  193. ###########################################
  194. sub layout { # Set/Get the layout object
  195.              # associated with this appender
  196. ###########################################
  197.     my($self, $layout) = @_;
  198.  
  199.         # Somebody wants to *set* the layout?
  200.     if($layout) {
  201.         $self->{layout} = $layout;
  202.  
  203.         # somebody wants a layout, but not set yet, so give 'em default
  204.     }elsif (! $self->{layout}) {
  205.         $self->{layout} = Log::Log4perl::Layout::SimpleLayout
  206.                                                 ->new($self->{name});
  207.  
  208.     }
  209.  
  210.     return $self->{layout};
  211. }
  212.  
  213. ##################################################
  214. sub filter { # Set filter
  215. ##################################################
  216.     my ($self, $filter) = @_;
  217.  
  218.     if($filter) {
  219.         print "Setting filter to $filter->{name}\n" if DEBUG;
  220.         $self->{filter} = $filter;
  221.     }
  222.  
  223.     return $self->{filter};
  224. }
  225.  
  226. ##################################################
  227. sub AUTOLOAD { # Relay everything else to the underlying Log::Dispatch object
  228. ##################################################
  229.     my $self = shift;
  230.  
  231.     no strict qw(vars);
  232.  
  233.     $AUTOLOAD =~ s/.*:://;
  234.  
  235.     return $self->{appender}->$AUTOLOAD(@_);
  236. }
  237.  
  238. ##################################################
  239. sub DESTROY {
  240. ##################################################
  241.     # just there because of AUTOLOAD
  242. }
  243.  
  244. 1;
  245.  
  246. __END__
  247.  
  248. =head1 NAME
  249.  
  250. Log::Log4perl::Appender - Log appender class
  251.  
  252. =head1 SYNOPSIS
  253.  
  254.   use Log::Log4perl;
  255.  
  256.       # Define a logger
  257.   my $logger = Log::Log4perl->get_logger("abc.def.ghi");
  258.  
  259.       # Define a layout
  260.   my $layout = Log::Log4perl::Layout::PatternLayout->new(
  261.                    "%d (%F:%L)> %m");
  262.  
  263.       # Define an appender
  264.   my $appender = Log::Log4perl::Appender->new(
  265.                    "Log::Dispatch::Screen",
  266.                    name => 'dumpy');
  267.  
  268.       # Set the appender's layout
  269.   $appender->layout($layout);
  270.   $logger->add_appender($appender);
  271.  
  272. =head1 DESCRIPTION
  273.  
  274. This class is a wrapper around the C<Log::Dispatch::*> collection of
  275. dispatchers, so they can be used by C<Log::Log4perl>. 
  276. The module hides the idiosyncrasies of C<Log::Dispatch>
  277. (e.g. every dispatcher gotta have a name, but there's no 
  278. accessor to retrieve it)
  279. from C<Log::Log4perl> and yet re-uses the extremely useful 
  280. variety of dispatchers already created and tested
  281. in C<Log::Dispatch>.
  282.  
  283. =head1 FUNCTIONS
  284.  
  285. =head2 Log::Dispatch::Appender->new($dispatcher_class_name, ...);
  286.  
  287. The constructor C<new()> takes the name of the C<Log::Dispatcher>
  288. class to be created as a I<string> (!) argument, optionally followed by 
  289. a number of C<Log::Dispatcher::Whatever>-specific parameters,
  290. for example:
  291.  
  292.       # Define an appender
  293.   my $appender = Log::Log4perl::Appender->new("Log::Dispatch::File"
  294.                                               name => 'dumpy',
  295.                                               file => 'out.log');
  296.  
  297. If no C<name> parameter is specified, the appender object will create
  298. a unique one (format C<appNNN>), which can be retrieved later via
  299. the C<name()> method:
  300.  
  301.   print "The appender's name is ", $appender->name(), "\n";
  302.  
  303. Other parameters are specific to the C<Log::Dispatch> module being used.
  304. In the case above, the C<file> parameter specifies the name of 
  305. the C<Log::Dispatch::File> dispatcher used. 
  306.  
  307. However, if you're using a C<Log::Dispatch::Email> dispatcher to send you 
  308. email, you'll have to specify C<from> and C<to> email addresses.
  309. Every dispatcher is different.
  310. Please check the C<Log::Dispatch::*> documentation for the appender used
  311. for details on specific requirements.
  312.  
  313. The C<new()> method will just pass these parameters on to a newly created
  314. C<Log::Dispatch::*> object of the specified type.
  315.  
  316. When it comes to logging, the C<Log::Log4perl::Appender> will transparently
  317. relay all messages to the C<Log::Dispatch::*> object it carries 
  318. in its womb.
  319.  
  320. =head2 $appender->layout($layout);
  321.  
  322. The C<layout()> method sets the log layout
  323. used by the appender to the format specified by the 
  324. C<Log::Log4perl::Layout::*> object which is passed to it as a reference.
  325. Currently there's two layouts available:
  326.  
  327.     Log::Log4perl::Layout::SimpleLayout
  328.     Log::Log4perl::Layout::PatternLayout
  329.  
  330. Please check the L<Log::Log4perl::Layout::SimpleLayout> and 
  331. L<Log::Log4perl::Layout::PatternLayout> manual pages for details.
  332.  
  333. =head1 Supported Appenders 
  334.  
  335. Here's the list of appender modules currently available via C<Log::Dispatch>,
  336. if not noted otherwise, written by Dave Rolsky:
  337.  
  338.        Log::Dispatch::ApacheLog
  339.        Log::Dispatch::DBI (by Tatsuhiko Miyagawa)
  340.        Log::Dispatch::Email,
  341.        Log::Dispatch::Email::MailSend,
  342.        Log::Dispatch::Email::MailSendmail,
  343.        Log::Dispatch::Email::MIMELite
  344.        Log::Dispatch::File
  345.        Log::Dispatch::FileRotate (by Mark Pfeiffer)
  346.        Log::Dispatch::Handle
  347.        Log::Dispatch::Screen
  348.        Log::Dispatch::Syslog
  349.        Log::Dispatch::Tk (by Dominique Dumont)
  350.  
  351. C<Log4perl> doesn't care which ones you use, they're all handled in 
  352. the same way via the C<Log::Log4perl::Appender> interface.
  353. Please check the well-written manual pages of the 
  354. C<Log::Dispatch> hierarchy on how to use each one of them.
  355.  
  356. =head1 Pitfalls
  357.  
  358. Since the C<Log::Dispatch::File> appender truncates log files by default,
  359. and most of the time this is I<not> what you want, we've instructed 
  360. C<Log::Log4perl> to change this behaviour by slipping it the 
  361. C<mode =E<gt> append> parameter behind the scenes. So, effectively
  362. with C<Log::Log4perl> 0.23, a configuration like
  363.  
  364.     log4perl.category = INFO, FileAppndr
  365.     log4perl.appender.FileAppndr          = Log::Dispatch::File
  366.     log4perl.appender.FileAppndr.filename = test.log
  367.     log4perl.appender.FileAppndr.layout   = Log::Log4perl::Layout::SimpleLayout
  368.  
  369. will always I<append> to an existing logfile C<test.log> while if you 
  370. specifically request clobbering like in
  371.  
  372.     log4perl.category = INFO, FileAppndr
  373.     log4perl.appender.FileAppndr          = Log::Dispatch::File
  374.     log4perl.appender.FileAppndr.filename = test.log
  375.     log4perl.appender.FileAppndr.mode     = write
  376.     log4perl.appender.FileAppndr.layout   = Log::Log4perl::Layout::SimpleLayout
  377.  
  378. it will overwrite an existing log file C<test.log> and start from scratch.
  379.  
  380. =head1 Appenders Expecting Message Chunks
  381.  
  382. Instead of simple strings, certain appenders are expecting multiple fields
  383. as log messages. If a statement like 
  384.  
  385.     $logger->debug($ip, $user, "signed in");
  386.  
  387. causes an off-the-shelf C<Log::Log4perl::Screen> 
  388. appender to fire, the appender will 
  389. just concatenate the three message chunks passed to it
  390. in order to form a single string.
  391. The chunks will be separated by a string defined in 
  392. C<$Log::Log4perl::JOIN_MSG_ARRAY_CHAR> (defaults to the empty string
  393. ""). 
  394.  
  395. However, different appenders might choose to 
  396. interpret the message above differently: An
  397. appender like C<Log::Log4perl::Appender::DBI> might take the
  398. three arguments passed to the logger and put them in three separate
  399. rows into the DB.
  400.  
  401. The  C<warp_message> appender option is used to specify the desired 
  402. behaviour.
  403. If no setting for the appender property
  404.  
  405.     # *** Not defined ***
  406.     # log4perl.appender.SomeApp.warp_message
  407.  
  408. is defined in the Log4perl configuration file, the
  409. appender referenced by C<SomeApp> will fall back to the standard behaviour
  410. and join all message chunks together, separating them by
  411. C<$Log::Log4perl::JOIN_MSG_ARRAY_CHAR>.
  412.  
  413. If, on the other hand, it is set to a false value, like in
  414.  
  415.     log4perl.appender.SomeApp.layout=NoopLayout
  416.     log4perl.appender.SomeApp.warp_message = 0
  417.  
  418. then the message chunks are passed unmodified to the appender as an
  419. array reference. Please note that you need to set the appender's
  420. layout to C<Log::Log4perl::Layout::NoopLayout> which just leaves 
  421. the messages chunks alone instead of formatting them or replacing
  422. conversion specifiers.
  423.  
  424. B<Please note that the standard appenders in the Log::Dispatch hierarchy
  425. will choke on a bunch of messages passed to them as an array reference. 
  426. You can't use C<warp_message = 0> (or the function name syntax
  427. defined below) on them.
  428. Only special appenders like Log::Log4perl::Appender::DBI can deal with
  429. this.>
  430.  
  431. If (and now we're getting fancy)
  432. an appender expects message chunks, but we would 
  433. like to pre-inspect and probably modify them before they're 
  434. actually passed to the appender's C<log>
  435. method, an inspection subroutine can be defined with the
  436. appender's C<warp_message> property:
  437.  
  438.     log4perl.appender.SomeApp.layout=NoopLayout
  439.     log4perl.appender.SomeApp.warp_message = sub { \
  440.                                            $#_ = 2 if @_ > 3; \
  441.                                            return @_; }
  442.  
  443. The inspection subroutine defined by the C<warp_message> 
  444. property will receive the list of message chunks, like they were
  445. passed to the logger and is expected to return a corrected list.
  446. The example above simply limits the argument list to a maximum of
  447. three by cutting off excess elements and returning the shortened list.
  448.  
  449. Also, the warp function can be specified by name like in
  450.  
  451.     log4perl.appender.SomeApp.layout=NoopLayout
  452.     log4perl.appender.SomeApp.warp_message = main::filter_my_message
  453.  
  454. In this example,
  455. C<filter_my_message> is a function in the C<main> package, 
  456. defined like this:
  457.  
  458.     my $COUNTER = 0;
  459.  
  460.     sub filter_my_message {
  461.         my @chunks = @_;
  462.         unshift @chunks, ++$COUNTER;
  463.         return @chunks;
  464.     }
  465.  
  466. The subroutine above will add an ever increasing counter
  467. as an additional first field to 
  468. every message passed to the C<SomeApp> appender -- but not to
  469. any other appender in the system.
  470.  
  471. =head1 SEE ALSO
  472.  
  473. Log::Dispatch
  474.  
  475. =head1 AUTHOR
  476.  
  477. Mike Schilli, E<lt>log4perl@perlmeister.comE<gt>
  478.  
  479. =cut
  480.