terom@45: from pvl.syslog.filter import SyslogFilter terom@45: terom@45: import re terom@45: terom@51: import optparse, sys terom@55: import configobj terom@51: terom@45: import logging; log = logging.getLogger('pvl.syslog.rule') terom@45: terom@51: def parser (parser) : terom@51: """ terom@51: Optparse option group. terom@51: """ terom@51: terom@51: syslog_rules = optparse.OptionGroup(parser, "Syslog rules") terom@51: terom@51: syslog_rules.add_option('--syslog-rules', metavar='FILE', terom@51: help="Load syslog rules from file") terom@51: terom@51: return syslog_rules terom@51: terom@51: def apply (options) : terom@51: """ terom@51: Build SyslogRules from options. terom@51: """ terom@51: terom@51: if options.syslog_rules : terom@55: return SyslogRule.load(open(options.syslog_rules)) terom@51: terom@51: else : terom@62: return SyslogRule('default', formats={ 'default': '{msg}' }) terom@51: terom@51: # TODO: combine SyslogRule/Rules into one heirarchial SyslogRule -type? terom@45: class SyslogRule (object) : terom@45: """ terom@55: A named SyslogFilter with sub-rules. terom@45: """ terom@51: terom@55: @classmethod terom@55: def load (cls, file) : terom@55: """ terom@55: Load SyslogRule from file. terom@55: """ terom@45: terom@55: config = configobj.ConfigObj(file) terom@66: terom@66: return cls.config_section(file.name, config) terom@66: terom@66: @classmethod terom@66: def config_section (cls, name, section) : terom@66: """ terom@66: Recursively load Syslogrules from config section. terom@66: """ terom@66: terom@66: rules = [cls.config_section(subsection, section[subsection]) for subsection in section.sections] terom@66: attrs = dict((name, section[name]) for name in section.scalars) terom@66: terom@66: return cls.config(name, rules=rules, **attrs) terom@45: terom@55: @classmethod terom@58: def config (cls, name, format=None, program=None, pattern=None, rules=None, **filters) : terom@55: """ terom@55: Build SyslogRule from config options terom@55: """ terom@55: terom@55: if pattern : terom@55: pattern = re.compile(pattern) terom@55: terom@58: if format : terom@58: format = { name: format } terom@55: terom@55: filters = dict( terom@58: (attr, re.compile(regex)) for attr, regex in filters.iteritems() terom@55: ) terom@55: terom@58: if program : terom@58: filters['prog'] = program terom@55: terom@58: if pattern : terom@58: filters['msg'] = pattern terom@55: terom@58: filter = SyslogFilter(**filters) terom@66: terom@66: log.debug("%s: %s %s", name, rules, filter) terom@58: terom@58: return cls(name, rules, filter, format) terom@58: terom@66: def __init__ (self, name, rules=None, filter=None, formats=None) : terom@55: self.name = name terom@55: self.rules = rules or [] # sub-rules terom@55: self.filter = filter # SyslogFilter terom@66: self.formats = formats or {} terom@55: terom@55: def match (self, item) : terom@55: """ terom@55: Match item against our filter, applying any matches. terom@55: """ terom@55: terom@55: if self.filter : terom@55: # filter terom@55: matches = self.filter.filter(item) terom@55: terom@55: else : terom@55: # match all terom@55: matches = True terom@55: terom@58: log.debug("%s: %s", self, matches) terom@58: terom@58: if not matches : terom@58: # ignore terom@58: return None terom@58: terom@55: # apply terom@55: item['rule'] = self terom@55: terom@55: if matches is True : terom@55: # no-op filter terom@55: pass terom@55: else : terom@55: item.update(matches) terom@55: terom@55: # XXX: copy, not mutate? terom@55: return item terom@58: terom@58: def format (self, item) : terom@58: """ terom@58: Apply output formats terom@58: """ terom@66: terom@66: out = {} terom@58: terom@58: for attr, format in self.formats.iteritems() : terom@66: value = out[attr] = format.format(**item) terom@58: log.debug("%s: %s: %s", self, attr, value) terom@58: terom@66: return out terom@58: terom@55: # TODO: __call__? terom@45: def apply (self, item) : terom@45: """ terom@66: Match item against ourself, apply against any sub-rules, and return output, if hit. terom@45: """ terom@45: terom@55: item = self.match(item) terom@45: terom@55: if not item : terom@66: # skip terom@55: return terom@55: terom@55: # sub-rules terom@55: for rule in self.rules : terom@55: try : terom@66: out = rule.apply(item) terom@55: terom@55: except Exception as ex : terom@55: log.exception("rule %s: %r", rule, item) terom@55: return # XXX: skip? terom@55: terom@66: if out : terom@66: # hit terom@66: return out terom@45: terom@55: # TODO: routing/formatting terom@58: #if self.tag is False : terom@58: # # drop terom@58: # return False terom@58: terom@58: if self.formats : terom@66: # hit terom@66: item.update(self.format(item)) terom@66: terom@55: return item terom@51: terom@55: def __iter__ (self, items) : terom@47: """ terom@47: Apply items against our rules, yielding any matches. terom@47: """ terom@47: terom@47: for item in items : terom@47: match = self.apply(item) terom@47: terom@47: if match : terom@47: yield match terom@55: terom@55: def __str__ (self) : terom@55: return self.name terom@55: terom@55: def __repr__ (self) : terom@55: return 'SyslogRule({self.name}, ...)'.format(self=self) terom@55: