diff options
Diffstat (limited to 'lib/Log/Log4perl/Filter/Boolean.pm')
-rw-r--r-- | lib/Log/Log4perl/Filter/Boolean.pm | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/lib/Log/Log4perl/Filter/Boolean.pm b/lib/Log/Log4perl/Filter/Boolean.pm new file mode 100644 index 0000000..21201d4 --- /dev/null +++ b/lib/Log/Log4perl/Filter/Boolean.pm @@ -0,0 +1,211 @@ +################################################## +package Log::Log4perl::Filter::Boolean; +################################################## + +use 5.006; + +use strict; +use warnings; + +use Log::Log4perl::Level; +use Log::Log4perl::Config; + +use constant _INTERNAL_DEBUG => 0; + +use base qw(Log::Log4perl::Filter); + +################################################## +sub new { +################################################## + my ($class, %options) = @_; + + my $self = { params => {}, + %options, + }; + + bless $self, $class; + + print "Compiling '$options{logic}'\n" if _INTERNAL_DEBUG; + + # Set up meta-decider for later + $self->compile_logic($options{logic}); + + return $self; +} + +################################################## +sub ok { +################################################## + my ($self, %p) = @_; + + return $self->eval_logic(\%p); +} + +################################################## +sub compile_logic { +################################################## + my ($self, $logic) = @_; + + # Extract Filter placeholders in logic as defined + # in configuration file. + while($logic =~ /([\w_-]+)/g) { + # Get the corresponding filter object + my $filter = Log::Log4perl::Filter::by_name($1); + die "Filter $filter required by Boolean filter, but not defined" + unless $filter; + + $self->{params}->{$1} = $filter; + } + + # Fabricate a parameter list: A1/A2/A3 => $A1, $A2, $A3 + my $plist = join ', ', map { '$' . $_ } keys %{$self->{params}}; + + # Replace all the (dollar-less) placeholders in the code with + # calls to their respective coderefs. + $logic =~ s/([\w_-]+)/\&\$$1/g; + + # Set up the meta decider, which transforms the config file + # logic into compiled perl code + my $func = <<EOT; + sub { + my($plist) = \@_; + $logic; + } +EOT + + print "func=$func\n" if _INTERNAL_DEBUG; + + my $eval_func = eval $func; + + if(! $eval_func) { + die "Syntax error in Boolean filter logic: $eval_func"; + } + + $self->{eval_func} = $eval_func; +} + +################################################## +sub eval_logic { +################################################## + my($self, $p) = @_; + + my @plist = (); + + # Eval the results of all filters referenced + # in the code (although the order of keys is + # not predictable, it is consistent :) + for my $param (keys %{$self->{params}}) { + # Pass a coderef as a param that will run the filter's ok method and + # return a 1 or 0. + print "Passing filter $param\n" if _INTERNAL_DEBUG; + push(@plist, sub { + return $self->{params}->{$param}->ok(%$p) ? 1 : 0 + }); + } + + # Now pipe the parameters into the canned function, + # have it evaluate the logic and return the final + # decision + print "Passing in (", join(', ', @plist), ")\n" if _INTERNAL_DEBUG; + return $self->{eval_func}->(@plist); +} + +1; + +__END__ + +=encoding utf8 + +=head1 NAME + +Log::Log4perl::Filter::Boolean - Special filter to combine the results of others + +=head1 SYNOPSIS + + log4perl.logger = WARN, AppWarn, AppError + + log4perl.filter.Match1 = sub { /let this through/ } + log4perl.filter.Match2 = sub { /and that, too/ } + log4perl.filter.MyBoolean = Log::Log4perl::Filter::Boolean + log4perl.filter.MyBoolean.logic = Match1 || Match2 + + log4perl.appender.Screen = Log::Dispatch::Screen + log4perl.appender.Screen.Filter = MyBoolean + log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout + +=head1 DESCRIPTION + +Sometimes, it's useful to combine the output of various filters to +arrive at a log/no log decision. While Log4j, Log4perl's mother ship, +chose to implement this feature as a filter chain, similar to Linux' IP chains, +Log4perl tries a different approach. + +Typically, filter results will not need to be passed along in chains but +combined in a programmatic manner using boolean logic. "Log if +this filter says 'yes' and that filter says 'no'" +is a fairly common requirement but hard to implement as a chain. + +C<Log::Log4perl::Filter::Boolean> is a special predefined custom filter +for Log4perl which combines the results of other custom filters +in arbitrary ways, using boolean expressions: + + log4perl.logger = WARN, AppWarn, AppError + + log4perl.filter.Match1 = sub { /let this through/ } + log4perl.filter.Match2 = sub { /and that, too/ } + log4perl.filter.MyBoolean = Log::Log4perl::Filter::Boolean + log4perl.filter.MyBoolean.logic = Match1 || Match2 + + log4perl.appender.Screen = Log::Dispatch::Screen + log4perl.appender.Screen.Filter = MyBoolean + log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout + +C<Log::Log4perl::Filter::Boolean>'s boolean expressions allow for combining +different appenders by name using AND (&& or &), OR (|| or |) and NOT (!) as +logical expressions. Parentheses are used for grouping. Precedence follows +standard Perl. Here's a bunch of examples: + + Match1 && !Match2 # Match1 and not Match2 + !(Match1 || Match2) # Neither Match1 nor Match2 + (Match1 && Match2) || Match3 # Both Match1 and Match2 or Match3 + +=head1 SEE ALSO + +L<Log::Log4perl::Filter>, +L<Log::Log4perl::Filter::LevelMatch>, +L<Log::Log4perl::Filter::LevelRange>, +L<Log::Log4perl::Filter::MDC>, +L<Log::Log4perl::Filter::StringRange> + +=head1 LICENSE + +Copyright 2002-2013 by Mike Schilli E<lt>m@perlmeister.comE<gt> +and Kevin Goess E<lt>cpan@goess.orgE<gt>. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=head1 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@lists.sourceforge.net + +Authors (please contact them via the list above, not directly): +Mike Schilli <m@perlmeister.com>, +Kevin Goess <cpan@goess.org> + +Contributors (in alphabetical order): +Ateeq Altaf, Cory Bennett, Jens Berthold, Jeremy Bopp, Hutton +Davidson, Chris R. Donnelly, Matisse Enzer, Hugh Esco, Anthony +Foiani, James FitzGibbon, Carl Franks, Dennis Gregorovic, Andy +Grundman, Paul Harrington, Alexander Hartmaier David Hull, +Robert Jacobson, Jason Kohles, Jeff Macdonald, Markus Peter, +Brett Rann, Peter Rabbitson, Erik Selberg, Aaron Straup Cope, +Lars Thegler, David Viner, Mac Yang. + |