diff -r 96d204b146b4 -r f04df0db2acd pvl/syslog/filter.py --- a/pvl/syslog/filter.py Fri Jan 04 18:45:59 2013 +0200 +++ b/pvl/syslog/filter.py Fri Jan 04 19:12:24 2013 +0200 @@ -1,74 +1,111 @@ import logging; log = logging.getLogger('pvl.syslog.filter') +import re # XXX +import os.path, fnmatch + class SyslogFilter (object) : """ - Match syslog messages. - - XXX: do we need more than just prog? + Match syslog messages fields against given patterns. """ - def __init__ (self, prog=None) : + def __init__ (self, **filters) : """ - prog - match tag (process name) + Match using given { field: regex }. """ - self.prog = prog + self.filters = filters - def match_prog (self, prog) : + def match_prog (self, attr, filter, prog=None) : """ - Match given prog? + XXX: Match given prog as glob? """ - if not self.prog : - return + if not filter : + # ignore + return None # normalize - prog = prog.lower() - - if prog.startswith('/') : - # base - prog = prog.split('/')[-1] + prog = prog.strip().lower() + _, prog = os.path.split(prog) # match - if not prog : - # never matches non-tagged lines + if fnmatch.fnmatch(prog, filter) : + return { attr: prog } + else : return False - elif self.prog.endswith('*') : - # prefix match - return prog.startswith(self.prog[:-1]) + def match (self, attr, regex, value=None) : + """ + Match given value against given pattern. + """ + if not filter : + # ignore + return None + + if not value : + # XXX: optional = match empty string? + value = '' else : - return prog == self.prog + # normalize; XXX: unicode? + value = str(value).strip() + + # match + match = regex.match(value) + + if match : + # as matche-values + match = match.groupdict() + match[attr] = value + + return match def filter (self, item) : """ - Match given item? + Match given item. Returns any matched values (including regexp capture groups) across all fields. """ - for attr, func in ( - ( 'prog', self.match_prog), - ) : - match = func(item[attr]) + matches = {} + + for attr in self.filters : + # lookup match-func + match = getattr(self, 'match_{attr}'.format(attr=attr), self.match) + + # filter + filter = self.filters[attr] + + # apply match + if attr in item : + match = match(attr, filter, item[attr]) + else : + match = match(attr, filter) + + log.debug("%s: %s", attr, match) if match : - continue + # match + matches.update(match) elif match is None : - pass + # ignore + continue else : - break + # reject + return - # ok + # XXX: test last match, in case they were all None if match is None : # XXX: empty filter!? return True else : - return match + return matches - def __call__ (self, items) : + def process (self, items) : for item in items: - if self.filter(item) : + match = self.filter(item) + + if match : yield item - + + __call__ = process