fixbot/logwatch/sources.py
author Tero Marttila <terom@fixme.fi>
Thu, 04 Feb 2010 20:39:53 +0200
changeset 48 ba101beeb062
parent 40 b9fdb7710768
child 51 342850300d6a
permissions -rw-r--r--
work on logwatch docs, small tweaks
"""
    Implementations of the various sources of log data
"""

from twisted.internet import protocol
from twisted.python import log

from fixbot import fifo

class LogSource (object) :
    """
        Reads lines of log data from some file or other source.
    """

    def __init__ (self, name, filters) :
        """
            name            - label lines read from this source
            filters         - LogFilter chain to pass lines through
        """

        # set later on
        self.module = None
        
        # what filters to apply
        self.filters = filters
        
        # name, for display purposes
        self.name = name

        # used to gather data together into lines
        self.buf = ""

    def setModule (self, module) :
        self.module = module

    def handleError (self, msg) :
        log.err(msg)
        self.module.error(msg)

    def handleData (self, data) :
        """
            Buffer the given chunk of data, passing any full lines to handleLine
        """

        data = self.buf + data
        
        while "\n" in data :
            line, data = data.split("\n", 1)

            self.handleLine(line)

        self.buf = data

    def handleLine (self, line) :
        log.msg("Matching line `%s'..." % line)

        for filter in self.filters :
            # let the filter process the line
            out = filter.test(line)

            if out :
                # unpack
                type, msg = out

                # positive match, send
                log.msg("\t%s: %s" % (type, msg))
                
                # drop until we have a module
                if self.module :
                    self.module.sendEvent(type, msg)
                
                # ok, first hit does it
                break

            elif out is False :
                # negative match, stop processing
                return

            elif out is None :
                # no match
                continue

            else :
                raise ValueError(out)

class File (LogSource, protocol.ProcessProtocol) :
    """
        Stream lines from a regular file using /usr/bin/tail -f
    """

    def __init__ (self, name, path, filters) :
        super(File, self).__init__(name, filters)

        self.path = path

        log.msg("spawning tail process for %s:%s" % (name, path))

        reactor.spawnProcess(self, "/usr/bin/tail", ["tail", "-n0", "--follow=name", path])

    def errReceived (self, data) :
        self.handleError("tail for %s: %s" % (self.name, data))

    def outReceived (self, data) :
        self.handleData(data)

    def processEnded (self, reason) :
        self.handleError("tail process for %s quit: %s" % (self.name, reason.getErrorMessage()))

class Fifo (LogSource, fifo.Fifo) :
    """
        Stream lines from a fifo object.
    """

    def __init__ (self, name, path, filters) :
        LogSource.__init__(self, name, filters)

        log.msg("opening fifo for %s:%s" % (name, path))

        fifo.Fifo.__init__(self, path)
    
    def dataReceived (self, data) :
        self.handleData(data)

    def handleEOF (self) :
        self.handleError("!!! EOF on fifo %s, re-opening" % self.name)
        self.reopen()
    
    def connectionLost (self, reason) :
        super(Fifo, self).connectionLost(reason)
        self.handleError("lost fifo for %s: %s" % (self.name, reason.getErrorMessage()))