pvl.args: load default config from /etc/pvl/package/module.conf, if specified to parse(...)
from pvl.syslog.filter import SyslogFilter
import re
import optparse, sys
import configobj
import logging; log = logging.getLogger('pvl.syslog.rule')
def parser (parser) :
"""
Optparse option group.
"""
syslog_rules = optparse.OptionGroup(parser, "Syslog rules")
syslog_rules.add_option('--syslog-rules', metavar='FILE',
help="Load syslog rules from file")
return syslog_rules
def apply (options) :
"""
Build SyslogRules from options.
"""
if options.syslog_rules :
return SyslogRule.load(open(options.syslog_rules))
else :
return SyslogRule('default', formats={ 'text': '{msg}' })
def merge (*dicts, **kwargs) :
return dict((k, v) for d in (dicts + (kwargs, )) for k, v in d.iteritems())
# TODO: combine SyslogRule/Rules into one heirarchial SyslogRule -type?
class SyslogRule (object) :
"""
A named SyslogFilter with sub-rules.
"""
@classmethod
def load (cls, file) :
"""
Load SyslogRule from file.
"""
config = configobj.ConfigObj(file)
return cls.config_section(file.name, config)
@classmethod
def config_section (cls, name, section) :
"""
Recursively load Syslogrules from config section.
"""
rules = [cls.config_section(subsection, section[subsection]) for subsection in section.sections]
attrs = dict((name, section[name]) for name in section.scalars)
try :
return cls.config(name, rules, **attrs)
except ValueError as ex :
raise ValueError("[%s] %s" % (name, ex))
@classmethod
def config_filters (cls, program=None, facility=None, pattern=None, **filters) :
"""
Return filter expression from given attr/value in config.
"""
# XXX: get rid of these special cases
if facility :
yield 'facility', facility # glob
if program :
yield 'prog', program # glob
if pattern :
filters['msg'] = pattern
# generic
for attr, value in filters.iteritems() :
try :
# regex
yield attr, re.compile(value)
except re.error as ex :
raise ValueError("%s: %s" % (attr, ex))
@classmethod
def config (cls, name, rules=None, format=None, irk=None, **filters) :
"""
Build SyslogRule from config options
"""
if format is not None :
format = { 'text': format }
else :
format = { }
if irk :
format['irk'] = irk
filters = dict(cls.config_filters(**filters))
filter = SyslogFilter(filters)
log.debug("%s: %s %s", name, rules, filter)
return cls(name, rules, filter, format)
def __init__ (self, name, rules=None, filter=None, formats=None) :
self.name = name
self.rules = rules or [] # sub-rules
self.filter = filter # SyslogFilter
self.formats = formats or {}
def match (self, item) :
"""
Match item against our filter, returning match-dict (empty?) or None.
"""
if self.filter :
# filter
matches = self.filter.filter(item)
else :
# match all, we probably have sub-rules that we're interested in
return { }
log.debug("%s: %s", self, matches)
if matches :
return matches
else :
# no match
return None
def format (self, item) :
"""
Apply our output formats to given base apply, yielding (unique) attr, value tuples.
"""
for attr, format in self.formats.iteritems() :
value = format.format(**item)
log.debug("%s: %s: %s", self, attr, value)
yield attr, value
def apply (self, item) :
"""
Recursively match item against ourself and sub-rules. Returns applied output.
Matches are passed down the tree, and applies are passed up.
"""
log.debug("%s", self)
# match rule -> matches
matches = self.match(item)
if matches is None :
# skip
return None, None, None
# merge matches down
item = merge(item, matches)
# recursive sub-rules -> apply
for rule in self.rules :
try :
# pass matches down
match, rules, apply = rule.apply(item)
except Exception as ex :
log.exception("%s -> %s: %r", self, rule, item)
continue # XXX: skip?
if apply :
# pass apply up
break
else :
# self-match
match, rules, apply = item, [], { }
rules.append(self)
# formats?
if self.formats :
# merge apply up
apply = merge(dict(self.format(item)), apply)
log.debug("%s: %s", '/'.join(str(rule) for rule in rules), apply)
return match, rules, apply
def __iter__ (self, items) :
"""
Apply items against our rules, yielding any matches.
"""
for item in items :
match, rules, apply = self.apply(item)
if apply :
yield apply
def __str__ (self) :
return self.name
def __repr__ (self) :
return 'SyslogRule({self.name}, ...)'.format(self=self)