--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fixbot/logwatch/message.py Fri Feb 05 20:37:51 2010 +0200
@@ -0,0 +1,173 @@
+"""
+ Log messages (e.g. syslog)
+"""
+
+# stdlib syslog, for facilities and levels
+import syslog
+import time, datetime
+import re
+
+PRIORITY_NAMES = {
+ syslog.LOG_EMERG: 'emerg',
+ syslog.LOG_ALERT: 'alert',
+ syslog.LOG_CRIT: 'crit',
+ syslog.LOG_ERR: 'err',
+ syslog.LOG_WARNING: 'warning',
+ syslog.LOG_NOTICE: 'notice',
+ syslog.LOG_INFO: 'info',
+ syslog.LOG_DEBUG: 'debug',
+}
+
+FACILITY_NAMES = {
+ syslog.LOG_KERN: 'kern', # 0
+ syslog.LOG_USER: 'user', # 1
+ syslog.LOG_MAIL: 'mail', # 2
+ syslog.LOG_DAEMON: 'daemon', # 3
+ syslog.LOG_AUTH: 'auth', # 4
+ 5: 'syslog', # 5
+ syslog.LOG_LPR: 'lpr', # 6
+ syslog.LOG_NEWS: 'news', # 7
+ syslog.LOG_UUCP: 'uucp', # 8
+ syslog.LOG_CRON: 'cron', # 9
+ 10: 'authpriv', # 10
+ 11: 'ftp', # 11
+ 12: 'ntp', # 12
+ 13: 'audit', # 13
+ 14: 'alert', # 14
+ 15: 'clock', # 15
+ syslog.LOG_LOCAL0: 'local0', # 16
+ syslog.LOG_LOCAL1: 'local1', # 17
+ syslog.LOG_LOCAL2: 'local2', # 18
+ syslog.LOG_LOCAL3: 'local3', # 19
+ syslog.LOG_LOCAL4: 'local4', # 20
+ syslog.LOG_LOCAL5: 'local5', # 21
+ syslog.LOG_LOCAL6: 'local6', # 22
+ syslog.LOG_LOCAL7: 'local7', # 23
+}
+
+class SyslogMessage (object) :
+ """
+ A message from syslog with the following fields:
+
+ pri - (optional) raw integer priority field
+ priority - (optional) message priority as text
+ facility - (optional) message facility as text
+ timestamp - message timestamp as a datetime
+ hostname - originating hostname
+ tag - (optional) message tag including process name and id
+ program - (optional) process name part of message tag
+ pid - (optional) process ID part of message tag
+ text - the log message following the tag
+ message - the full log message including the tag
+ raw - the full syslog-format message
+ """
+
+ # the regular expression used to parse the lines
+ LINE_RE = re.compile(
+ # the priority field as in raw syslog messages
+ r"(?:<(?P<pri>\d+)>)?"
+
+ # the timestamp+hostname header
+ + r"(?P<timestamp>\w{3} [0-9 ]\d \d{2}:\d{2}:\d{2}) (?P<hostname>\S+)"
+
+ # the message, including possible tag/pid
+ + r" (?P<message>(?P<tag>(?P<program>[^:\]]+)(?:\[(?P<pid>\d+)\])?: )?(?P<text>.+))\n?"
+ )
+
+ # strptime format of timestamp
+ TIMESTAMP_FMT = "%b %d %H:%M:%S"
+
+ def _parse_pri (self, match) :
+ """
+ Parse the priority/facility from the given match object
+ """
+
+ # raw integer
+ self.pri = int(match.group('pri')) if match.group('pri') else None
+
+ if self.pri :
+ # unpack
+ priority = self.pri % 8
+ facility = self.pri // 8
+
+ # translate to names
+ self.priority = PRIORITY_NAMES.get(priority, str(priority))
+ self.facility = FACILITY_NAMES.get(facility, str(facility))
+
+ else :
+ self.priority = self.facility = None
+
+ def _parse_timestamp (self, match) :
+ """
+ Parse the timestamp field into a datetime.datetime from the given match object
+ """
+
+ timestamp = match.group('timestamp')
+
+ try :
+ ts = time.strptime(timestamp, self.TIMESTAMP_FMT)
+
+ except Exception, ex :
+ raise ValueError("Invalid timestamp: %s: %s" % (timestamp, ex))
+
+ # build timestamp
+ self.timestamp = datetime.datetime(
+ # fix year - strptime default is 1900
+ ts.tm_year if ts.tm_year != 1900 else time.localtime().tm_year,
+
+ # month, day, hour, minute, second
+ *ts[1:6]
+ )
+
+ def _parse_hostname (self, match) :
+ """
+ Parse the hostname from the given match object
+ """
+
+ # nothing much needed..
+ self.hostname = match.group('hostname')
+
+ def _parse_message (self, match) :
+ """
+ Parse the message with tag from the given match object
+ """
+
+ self.message = match.group('message')
+ self.tag = match.group('tag')
+ self.program = match.group('program')
+ self.pid = int(match.group('pid')) if match.group('pid') else None
+ self.text = match.group('text')
+
+ def __init__ (self, line) :
+ """
+ Construct this message from the given line
+ """
+
+ # apply regexp
+ match = self.LINE_RE.match(line)
+
+ if not match :
+ # fail
+ raise ValueError("Invalid syslog data format")
+
+ # unpack the various portions
+ self._parse_pri(match)
+ self._parse_timestamp(match)
+ self._parse_hostname(match)
+ self._parse_message(match)
+
+ # the raw line as matched
+ self.raw = match.group(0)
+
+ def __str__ (self) :
+ """
+ Format to default format
+ """
+
+ return "%s %s %s" % (self.timestamp.strftime(self.TIMESTAMP_FMT), self.hostname, self.message)
+
+ def __repr__ (self) :
+ return "pri=%s:%s, timestamp=%s, hostname=%s, tag=%s[%s], text=%r" % (
+ self.facility, self.priority, self.timestamp.isoformat(), self.hostname, self.program, self.pid, self.text
+ )
+