fixbot/logwatch/filters.py
author Tero Marttila <terom@fixme.fi>
Sat, 06 Nov 2010 16:01:42 +0200
changeset 66 eb0545ec03e7
parent 57 31e7421e98af
permissions -rw-r--r--
Fix filter names
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
    label = None
    
    def __init__ (self, label) :
        self.label = label

    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
                    (label, <str>)
                            - filter matched, pass formatted output with given label
        """
        
        # 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.label, line

class NullFilter (BaseFilter) :
    """
        A filter that drops every line matching a given regexp
    """
    
    def __init__ (self, pattern, flags=0) :
        # don't need an label

        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, label, pattern, format=None) :
        super(SimpleFilter, self).__init__(label)
        
        # 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.label, self.format % match.groupdict()
        
        else :
            # match as-is
            return self.label, 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 = r"^\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 = r"^\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 the same from su?
cron_killer = SyslogFilter('cron',
    program = "cron",
    drop    = True,
)

# drops `su nobody` output (from cron)
su_nobody_killer = SyslogFilter('su-nobody',
    program     = "su",
    pattern     = r"^(Successful su for nobody by root|\+ \?\?\? root:nobody)$",
    re_flags    = re.IGNORECASE,
    drop        = True
)