pvl.syslog: refactor SyslogFilter for extensive magics
authorTero Marttila <terom@fixme.fi>
Fri, 04 Jan 2013 19:12:24 +0200
changeset 52 f04df0db2acd
parent 51 96d204b146b4
child 53 685fd90bc610
pvl.syslog: refactor SyslogFilter for extensive magics
pvl/syslog/args.py
pvl/syslog/filter.py
--- a/pvl/syslog/args.py	Fri Jan 04 18:45:59 2013 +0200
+++ b/pvl/syslog/args.py	Fri Jan 04 19:12:24 2013 +0200
@@ -73,6 +73,7 @@
     )
 
     filter = SyslogFilter(
+        # glob pattern
         prog    = options.syslog_prog,
     )
 
--- 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