From: Kevin G. <ke...@go...> - 2003-02-22 17:04:52
|
Crimeny, sorry about the delay. Brutal couple of weeks, am finally reading the RFC and it rocks!! All right, Mike! It's very clean, and it puts a lot more control into the config file does than the log4j filters do. Some observations: 1) Regarding log4j compatibility, if someone implements log4j-style filters it's just a matter of specifying a different implementation class in the config, right? Hmm, except for the chaining thing, and that log4j filters return ACCEPT/DENY/NEUTRAL instead of true/false. Is there any way to differentiate? Is it worth calling our filters "pfilters" or "bfilter" (b for boolean)? It looks like the xml for a log4j filter is set up like this <appender name="UDPVENUS" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.SimpleLayout"/> <filter class="org.apache.log4j.filters.PriorityRangeFilter"> <param name="PriorityMin" value="WARN" /> </filter> <filter class="org.apache.log4j.filters.StringMatchFilter"> <param name="StringToMatch" value="security violation" /> </filter> </appender> so they're making filter definitions local to an appender instead of global to a config. That would imply that *if* log4j filters were available in a config they might look like this: log4j.appender.Screen = org.apache.log4j.ConsoleAppender log4j.appender.Screen.Layout = org.apache.log4j.SimpleLayout log4j.appender.Screen.Filter.prf = org.apache.log4j.filters.PriorityRangeFilter log4j.appender.Screen.Filter.prf.PriorityMin = WARN log4j.appender.Screen.Filter.sm = org.apache.log4j.filters.StringMatchFilter log4j.appender.Screen.Filter.sm.StringToMatch = security violation Which implies a set of per-appender filters, chained. Whereas in our scheme chaining would be accomplished via a Bool filter. So we might have filters returning two sets of things, and one kind of filters link together in different ways. Is it worthwhile renaming ours to head off incompatability with the mother ship? It definitely shows the attraction of namespaces in XML! 2) As I was writing those examples, I wondered if it's worthwhile setting up to allow defining a filter per-appender as well as global, like we do for the cspec's? (see t/033UsrCspec.t). Then you can use them close to where they're defined. 3) There seems to be a typo in the email, under "Attaching a filter to an appender" it says log4perl.appender.MyAppender = MyFilter s/b log4perl.appender.MyAppender.Filter = MyFilter right? 4) Tiny point: do you really want to call it Bool.pm instead of Boolean.pm? What's a Bool? 5) Why is "f" capitalized in "log4perl.appender.Screen.Filter" but not in "log4perl.filter.Match1"? (I'm as guilty as anyone of inconistency here, but it's never to late to reform!) 6) log4j's decide() method returns a constant ACCEPT/DENY/NEUTRAL, but our decide() returns a boolean. Wouldn't it be better to name our method let_pass() or is_loggable() or something that implies how the boolean will be treated? ("Hmm, I can't remember, does decide()==true mean decides to filter it out, or decides to log it?"). I think a hint to the developer might be appreciated some day. msc...@ao... wrote: > > Hey Kevin and all, > > here's a design proposal for the soon-to-come "custom filters" Log4perl feature. Let me know how if you like it! To be clear, nothing of what's mentioned below has been implemented yet (it's checked in under Log/Log4perl/Filter.pm, though) -- just wanted to run this by you to make sure I haven't overlooked anything. Comments, questions, counter-proposal welcome, as always :) > > -- > -- Mike > > ############################ > # Mike Schilli # > # log...@pe... # > # http://perlmeister.com # > # log4perl.sourceforge.net # > ############################ > > NAME > > Log::Log4perl::Filter - Log4perl Custom Filter Base Class > > SYNOPSIS > use Log::Log4perl; > > Log::Log4perl->init(<<'EOT'); > log4perl.logger = INFO, Screen > log4perl.filter.MyFilter = sub { /let this through/ } > log4perl.appender.Screen = Log::Dispatch::Screen > log4perl.appender.Screen.Filter = MyFilter > log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout > EOT > > # Define a logger > my $logger = Log::Log4perl->get_logger("Some"); > > # Let this through > $logger->info("Here's the info, let this through!"); > > # Suppress this > $logger->info("Here's the info, suppress this!"); > > DESCRIPTION > Log4perl allows the use of customized filters in its appenders to > control the output of messages. These filters might grep for certain > text chunks in a message, verify that its priority matches or exceeds a > certain level or that this is the 10th time the same message has been > submitted -- and come to a log/no log decision based upon these > circumstantial facts. > > Filters carry names and can be specified in two different ways in the > Log4perl configuration file: As subroutines or as filter classes. Here's > a simple filter named "MyFilter" which just verifies that the oncoming > message matches the regular expression "/let this through/i": > > log4perl.filter.MyFilter = sub { /let this through/i } > > It exploits the fact that when the filter is called on a message, Perl's > special $_ variable will be set to the (rendered) message to be logged. > The filter subroutine is expected to return a true value if it wants the > message to be logged or a false value if doesn't. Also, Log::Log4perl > will pass the same arguments to the filter function as it would to the > corresponding appender. Here's an example of a filter checking the > priority of the oncoming message: > > log4perl.filter.MyFilter = sub { \ > my %p = @_; \ > (%p{log4p_level} == WARN) ? 1 : 0; \ > } > > If the message priority equals "WARN", it returns a true value, causing > the message to be logged. For common tasks like this, there's already a > set of predefined filters available. To perform a level match, it's much > cleaner to use Log4perl's "LevelMatch" filter instead: > > log4perl.filter.MyFilter = Log::Log4perl::Filter::LevelMatch > log4perl.filter.MyFilter.LevelToMatch = WARN > > Once a filter has been defined by name and class, its values can be > assigned to its attributes, just as the "WARN" value to the > "LevelToMatch" attribute above. > > Attaching a filter to an appender > Attaching a filter to an appender is as easy as assigning its name to > the appender's "Filter" attribute: > > log4perl.appender.MyAppender = MyFilter > > This will cause "Log::Log4perl" to call the filter subroutine/method > every time a message is supposed to be passed to the appender. Depending > on the filter's return value, "Log::Log4perl" will either continue as > planned or withdraw. > > Combining filters with Log::Log4perl::Filter::Bool > Sometimes, it's useful to combine the output of various filters to > arrive at a log/no log deciscion. 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. > > "Log::Log4perl::Filter::Bool" 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.MyBool = Log::Log4perl::Filter::Bool > log4perl.filter.MyBool.logic = Match1 || Match2 > > log4perl.appender.Screen = Log::Dispatch::Screen > log4perl.appender.Screen.Filter = MyBool > log4perl.appender.Screen.layout = Log::Log4perl::Layout::SimpleLayout > > "Log::Log4perl::Filter::Bool"'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 > > Writing your own filter classes > If none of Log::Log4perl's predefined filter classes fits your needs, > you can easily roll your own: Just define a new class, derive it from > the base class "Log::Log4perl::Filter" and define its "decide" method: > > package Log::Log4perl::Filter::MyFilter; > use base Log::Log4perl::Filter; > > sub decide { > my ($self, %p) = @_; > > # ... decide and return 1 or 0 > } > > 1; > > Values you've defined for its attributes in Log4perl's configuration > file, it will receive through its "new" method: > > log4perl.filter.MyFilter = Log::Log4perl::Filter::MyFilter > log4perl.filter.MyFilter.color = red > > will cause "Log::Log4perl::Filter::MyFilter"'s constructor to be called > like this: > > Log::Log4perl::Filter::MyFilter->new( color => "red" ); > > which in turn should be used by the custom filter class to set the > object's attributes, which later on can be consulted inside the "decide" > call. > > A Practical Example: Level Matching > 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", and so forth. > > 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 is a custom filter for both appenders verifying that the > priority of the oncoming messages exactly *matches* the priority the > appender is supposed to log messages of: > > log4perl.logger = WARN, AppWarn, AppError > > # Filter to match level ERROR > log4perl.filter.MatchError = Log::Log4perl::Filter::LevelMatch > log4perl.filter.MatchError.LevelToMatch = ERROR > > # Filter to match level WARN > log4perl.filter.MatchWarn = Log::Log4perl::Filter::LevelMatch > log4perl.filter.MatchWarn.LevelToMatch = WARN > > # Error appender > log4perl.appender.AppError = Log::Dispatch::File > log4perl.appender.AppError.filename = /tmp/app.err > log4perl.appender.AppError.layout = SimpleLayout > log4perl.appender.AppError.Filter = MatchError > > # Warning appender > log4perl.appender.AppWarn = Log::Dispatch::File > log4perl.appender.AppWarn.filename = /tmp/app.warn > log4perl.appender.AppWarn.layout = SimpleLayout > log4perl.appender.AppWarn.Filter = MatchWarn > > This will direct WARN messages and /tmp/app.warn and ERROR messages to > /tmp/app.error without overlaps. > > SEE ALSO > AUTHOR > Mike Schilli, <log...@pe...>, 2003 > > ------------------------------------------------------- > This sf.net email is sponsored by:ThinkGeek > Welcome to geek heaven. > http://thinkgeek.com/sf > _______________________________________________ > log4perl-devel mailing list > log...@li... > https://lists.sourceforge.net/lists/listinfo/log4perl-devel -- Happy Trails. . . Kevin M. Goess (and Anne and Frank) 904 Carmel Ave. Albany, CA 94706 (510)525-5217 |