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