#!/usr/bin/env python
from optparse import OptionParser, Option
import time, sys
__cmdParser__ = OptionParser()
__cmdParser__.add_option("-i", "--logfile", action="append", \
dest="logfiles", type="string", \
help="system log to parse")
__cmdParser__.add_option("-v", "--verbose", action="count", \
dest="verbosity", \
help="How obnoxious we're being about telling the user what we're doing.")
(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args()
class logfile_class:
def __init__(self,fname):
self.events = []
self.curline = ""
self.prevline = ""
self.eof = False
self.hostname = ""
self.fname = fname
self.fp = open(fname)
first_line = self.fp.readline().strip()
multip = 1
readblock = 64
while True:
self.fp.seek(-readblock * multip,2)
newlnpos = self.fp.read(readblock).find("\n")
if newlnpos > 0 and newlnpos < readblock - 1:
self.fp.seek(-readblock * multip + newlnpos +1, 2)
break
multip+=1
last_line = self.fp.readline().strip()
self.fp.seek(0)
# let's convert the first and last timestamp to something useful
# Jul 22 04:48:05
self.time_begin = time.strptime(first_line[0:15], "%b %d %H:%M:%S")
self.time_end = time.strptime( last_line[0:15], "%b %d %H:%M:%S")
# FIXME: check that first_line < last_line
def readline(self):
self.curline_pos = self.fp.tell()
self.prevline = self.curline
self.curline = self.fp.readline().strip()
if len(self.curline) == 0:
self.eof = True
return self.curline
def seek(self,pos):
self.fp.seek(pos)
self.eof = False
self.curline = ""
def parse(self):
self.seek(0)
while not self.eof:
self.readline()
self.parse_line()
self.seek(0)
def curmessage(self):
return self.curline[17 + self.curline[16:].find(" "):]
def parse_line(self):
# is valid log line ?
if not self.time_current():
return
# store hostname, if we don't already have it
if len(self.hostname) == 0:
self.curline.split()[3]
# system is booting
if self.curmessage().startswith("kernel: Linux version"):
self.add_event("system boot")
# hostname has changed
if len(self.hostname) and self.hostname != self.curline.split()[3]:
self.add_event("hostname changed")
self.hostname = self.curline.split()[3]
# the clock is wrong wrong
if self.prevline and time.strptime(self.prevline[0:15], "%b %d %H:%M:%S") > self.time_current():
self.add_event("clock is messed up")
def add_event(self, message):
self.events.append( (self.curline_pos,len(self.curline),message) )
def time_current(self):
if len(self.curline) == 0: return None
try:
return time.strptime(self.curline[0:15], "%b %d %H:%M:%S")
except ValueError:
print "could not parse time", self.curline
return False
logs = []
for logname in __cmdLineOpts__.logfiles:
log = logfile_class(logname)
log.parse()
logs.append(log)
print """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<style type="text/css">
<!--
body {
font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
background: #E6EAE9;
}
a {
color: #c75f3e;
}
#mytable {
width: 97%;
padding: 0;
margin: 0;
}
caption {
padding: 0 0 5px 0;
width: 97%;
font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
text-align: right;
}
th {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #a50000 url(images/bg_header.jpg) no-repeat;
color: white;
}
th.nobg {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
color: black;
}
td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
padding: 6px 6px 6px 12px;
color: #4f6b72;
letter-spacing: -1px;
white-space:pre-wrap;
font-family: monospace;
}
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
td.alt {
background: #ffbaba;
color: #797268;
}
th.newday {
text-align: right;
padding: 2px;
}
th.spec {
border-left: 1px solid #C1DAD7;
border-right: 1px solid #C1DAD7;
border-top: 0;
background: #fff url(images/bullet1.gif) no-repeat;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
th.specalt {
border-left: 1px solid #C1DAD7;
border-right: 1px solid #C1DAD7;
border-top: 0;
background: #cecfce url(images/bullet2.gif) no-repeat;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
-->
</style>
</head>
<body>
"""
print '<ul id="toc">'
for log in logs:
# print logs[idx].fname, logs[idx].events
for line, msglen, event in log.events:
print ' <li><span>%s</span> <a href="#">Link</a><br /></li>' % event
print '</ul>'
print """
<table id="mytable" cellspacing="0" summary="The technical specifications of the Apple PowerMac G5 series">
<caption>Generated by SOS htmlogger 1.0 on March 16th 2008</caption>
"""
previous_date = None
inc = 0
while True:
# who is next ?
lowest_date = None
for log in logs:
if log.eof:
continue
if not len(log.curline):
log.readline()
if lowest_date == None or log.time_current() < lowest_date:
lowest_date = log.time_current()
if lowest_date == None:
# all logs are EOF
break
if inc == 0 or inc % 200 == 0:
print """<tr><th scope="col" abbr="Date/Time" class="nobg">Time of Event</th><th scope="col" abbr="node-1">node-1</th><th scope="col" abbr="node-2">node-2</th><th scope="col" abbr="node-3">node-3</th></tr>"""
if not previous_date or time.strftime("%b %d", previous_date) != time.strftime("%b %d", lowest_date):
print """<tr><th scope="row" class="newday" colspan=4>%s</th></tr>""" % time.strftime("%A, %B %d", lowest_date)
if inc % 2 == 0: row_class = "spec"
else: row_class = "altspec"
# FIXME: if this tick has an event, add <a name="...">
if not previous_date or previous_date != lowest_date:
print """<TR><th scope="row" class="spec">""" + time.strftime("%H:%M:%S", lowest_date) + """</th>"""
else:
print """<TR><th scope="row" class="spec" style="color: #cacaca">""" + time.strftime("%H:%M:%S", lowest_date) + """</th>"""
for log in logs:
if log.time_current() == lowest_date:
print " <TD>" + log.curmessage() + "</TD>"
log.curline = ""
else:
print " <TD></TD>"
# print log.curline_pos, time.strftime("%b %d %H:%M:%S", log.time_current()), log.curmessage()
print " </TR>"
previous_date = lowest_date
inc += 1
print "</table>"