import re
class BaseFilter (object) :
"""
A filter object matches incoming lines, to determine how they are handled, classify them, and optionally reformat them
"""
# the LogWatchModule event to send
event_type = None
def __init__ (self, event_type) :
self.event_type = event_type
def test (self, line) :
"""
Match against the given line. See match() for return codes
"""
raise NotImplementedError()
def match (self, msg) :
"""
Match against the given SyslogMessage, and return one of:
None - filter did not match, continue
False - filter matched, line should be dropped
(type, <str>)
- filter matched, pass formatted output
"""
# default to a full-line match
return self.test(str(msg))
class FullFilter (BaseFilter) :
"""
A trivial filter that matches every possible line as-is
"""
def test (self, line) :
# pass through
return self.event_type, line
class NullFilter (BaseFilter) :
"""
A filter that drops every line matching a given regexp
"""
def __init__ (self, pattern, flags=0) :
# don't need an event_type
self.regexp = re.compile(pattern, flags)
def test (self, line) :
match = self.regexp.search(line)
if match :
# drop
return False
class SimpleFilter (BaseFilter) :
"""
A simple filter that passes through any lines that match, optionally reformatting them with the given string
pattern, using the regexp match groups as parameters.
"""
def __init__ (self, event_type, pattern, format=None) :
super(SimpleFilter, self).__init__(event_type)
# store
self.regexp = re.compile(pattern)
self.format = format
def test (self, line) :
# match
match = self.regexp.search(line)
if not match :
# continue
return None
# reformat?
if self.format :
# format with regexp match groups
return self.event_type, self.format % match.groupdict()
else :
# match as-is
return self.event_type, line
class SyslogFilter (BaseFilter) :
"""
A more advanced filter that can match against fields in the syslog message.
"""
def __init__ (self, label, pattern=None, program=None, drop=False, format=None, re_flags=0) :
"""
Filter using the given criteria:
label - label match output with given label
pattern - match message content against given regexp
program - (optional) case-insensitive match against message tag's program component
May also be False to indicate no tag
drop - drop this message if this matches
format - (optional) format output with given format string
re_flags - (optional) flags for regular expression
"""
# XXX: super(SyslogFilter, self).__init__(label)
self.label = label
# store
if pattern :
self.regexp = re.compile(pattern, re_flags)
else :
self.regexp = None
self.program = program
self.drop = drop
self.format = format
def match (self, msg) :
"""
Evaluate match on given message
"""
# use the SyslogMessage's match method
match = msg.match(self.regexp, self.program)
# handle result
if match is False :
# nack
return None
elif self.drop :
# halt processing
return False
elif self.format :
# the messages properties
params = msg.properties()
# the regexp'd matched params
params.update(match)
# formatted output
return self.label, self.format % params
else :
# boring output
return self.label, str(msg)
# matches a timestamp prefix
_timestamp = "\w{3} [0-9 ]\d \d{2}:\d{2}:\d{2}"
# match all lines, but doesn't include the timestamp
all = SyslogFilter('all',
format = "%(hostname)s %(message)s"
)
# match sudo invocations, reformatting them nicely
sudo = SyslogFilter('sudo',
program = "sudo",
pattern = "^\s*(?P<username>\S+) : TTY=(?P<tty>\S+) ; PWD=(?P<pwd>.+?) ; USER=(?P<target_user>\S+) ; COMMAND=(?P<command>.*)",
format = "%(username)s:%(tty)s - %(target_user)s@%(hostname)s:%(pwd)s - %(command)r",
)
# match accepted ssh logins
ssh = SyslogFilter('ssh',
program = "sshd",
pattern = "^\s*Accepted password for (?P<username>\S+) from (?P<ip>\S+) port (?P<port>\S+) (?P<proto>\S+)",
format = "SSH login for %(username)s@%(hostname)s from %(ip)s:%(port)s",
)
# drops all output from cron
# XXX: what about su?
cron_killer = SyslogFilter('all',
program = "cron",
drop = True,
)
#cron_killer = NullFilter(
# "^" + _timestamp + " \S+\s+(CRON|su)\[\d+\]: pam_unix\(cron:\w+\): session (opened|closed) for user \w+( by \(uid=\d+\))?$",
# re.IGNORECASE
#)
# drops `su nobody` output (from cron)
su_nobody_killer = NullFilter(
"^" + _timestamp + " \S+\s+su\[\d+\]: (Successful su for nobody by root|\+ \?\?\? root:nobody)$",
re.IGNORECASE
)