aboutsummaryrefslogtreecommitdiffstats
path: root/src/extras/htmlog
diff options
context:
space:
mode:
Diffstat (limited to 'src/extras/htmlog')
-rwxr-xr-xsrc/extras/htmlog222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/extras/htmlog b/src/extras/htmlog
new file mode 100755
index 00000000..7765d09f
--- /dev/null
+++ b/src/extras/htmlog
@@ -0,0 +1,222 @@
+#!/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("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 fucked 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.0 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>HTMLogs output</TITLE>
+<style type="text/css">
+body {
+ background: #FFF;
+ color: #000;
+ font: normal normal 12px Verdana, Geneva, Arial, Helvetica, sans-serif;
+ margin: 10px;
+ padding: 0
+}
+
+table, td, a {
+ color: #000;
+ font: normal normal 12px Verdana, Geneva, Arial, Helvetica, sans-serif
+}
+
+/* make the TH elements pretty */
+thead.fixedHeader th {
+ background: #C96;
+ border-left: 1px solid #EB8;
+ border-right: 1px solid #B74;
+ border-top: 1px solid #EB8;
+ font-weight: normal;
+ padding: 4px 3px;
+ text-align: left
+}
+
+/* make the A elements pretty. makes for nice clickable headers */
+thead.fixedHeader a, thead.fixedHeader a:link, thead.fixedHeader a:visited {
+ color: #FFF;
+ display: block;
+ text-decoration: none;
+ width: 100%
+}
+
+/* make the A elements pretty. makes for nice clickable headers */
+/* WARNING: swapping the background on hover may cause problems in WinIE 6.x */
+thead.fixedHeader a:hover {
+ color: #FFF;
+ display: block;
+ text-decoration: underline;
+ width: 100%
+}
+
+ul#toc {list-style:none;width:320px;}
+#toc li {background:url(dot.gif) repeat-x 0 0.85em;}
+#toc li a {float:left;background:#FFF;padding: 0 4px 0 0;}
+#toc li span {float:right;background:#FFF; padding 0 0 0 4px;}
+#toc li br {clear:both;}
+
+-->
+</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>"
+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
+
+ print " <TR>"
+ # FIXME: if this tick has an event, add <a name="...">
+ print ' <TD STYLE="white-space:nowrap">' + time.strftime("%b %d %H:%M:%S", lowest_date) + "</TD>"
+ 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>"
+print "<TABLE>"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+