terom@50: """ terom@50: Format LogLines into some other representation terom@50: """ terom@50: terom@69: import re, xml.sax.saxutils terom@50: terom@50: from log_line import LogTypes terom@50: terom@50: class LogFormatter (object) : terom@50: """ terom@65: Provides a method to format series of LogLines into various output formats, with varying themes. terom@50: """ terom@51: terom@51: # the formatter's code name terom@51: name = None terom@50: terom@50: def __init__ (self, tz, timestamp_fmt="%H:%M:%S") : terom@50: """ terom@50: Initialize to format timestamps with the given timezone and timestamp terom@50: """ terom@50: terom@50: self.tz = tz terom@50: self.timestamp_fmt = timestamp_fmt terom@50: terom@65: def _format_line_text (self, line, template_dict, full_timestamp=False) : terom@50: """ terom@50: Format the given line as text, using the given { type: string template } dict terom@50: """ terom@65: terom@50: # look up the template terom@50: template = template_dict[line.type] terom@65: terom@65: # convert timestamp into display timezone terom@65: dtz = line.timestamp.astimezone(self.tz) terom@65: terom@65: # full timestamps? terom@65: if full_timestamp : terom@65: # XXX: ugly terom@65: timestamp_fmt = '%Y-%m-%d ' + self.timestamp_fmt terom@65: terom@65: else : terom@65: timestamp_fmt = self.timestamp_fmt terom@50: terom@50: # build timestamp terom@65: timestamp = dtz.strftime(timestamp_fmt) terom@50: terom@50: # format with dict terom@50: return template % dict( terom@50: timestamp = timestamp, terom@50: source = line.source, terom@50: data = line.data, terom@50: ) terom@50: terom@65: def format_txt (self, lines, full_timestamps=False) : terom@50: """ terom@65: Format given lines as plaintext. terom@65: terom@65: If full_timestamps is given, the output will contain full timestamps with both date and time. terom@65: terom@65: No trailing newlines. terom@50: """ terom@50: terom@50: abstract terom@50: terom@65: def format_html (self, lines, full_timestamps=False) : terom@50: """ terom@65: Format as HTML. terom@65: terom@65: See format_txt for information about arguments terom@50: """ terom@50: terom@50: abstract terom@50: terom@69: class BaseHTMLFormatter (object) : terom@69: """ terom@69: Implements some HTML-formatting utils terom@69: """ terom@69: terom@69: URL_REGEXP = re.compile(r"http://\S+") terom@69: terom@69: def _process_links (self, line) : terom@69: """ terom@69: Processed the rendered line, adding in 's for things that look like URLs, returning the new line. terom@69: terom@69: The line should already be escaped terom@69: """ terom@69: terom@69: def _encode_url (match) : terom@69: # encode URL terom@69: url_html = match.group(0) terom@69: url_link = xml.sax.saxutils.unescape(url_html) terom@69: terom@69: return '%(url_html)s' % dict(url_link=url_link, url_html=url_html) terom@69: terom@69: return self.URL_REGEXP.sub(_encode_url, line) terom@69: terom@50: class IrssiTextFormatter (LogFormatter) : terom@50: """ terom@50: Implements format_txt for irssi-style output terom@50: """ terom@50: terom@50: # format definitions by type terom@50: __FMT = { terom@50: LogTypes.RAW : "%(timestamp)s %(data)s", terom@50: } terom@50: terom@65: def format_txt (self, lines, full_timestamps=False) : terom@50: # ...handle each line terom@50: for line in lines : terom@50: # using __TYPES terom@65: yield self._format_line_text(line, self.__FMT, full_timestamps) terom@50: terom@69: class IrssiFormatter (IrssiTextFormatter, BaseHTMLFormatter) : terom@50: """ terom@50: Implements plain black-and-white irssi-style formatting terom@50: """ terom@50: terom@51: name = 'irssi' terom@59: title = "Irssi (plain)" terom@51: terom@65: def format_html (self, lines, full_timestamps=False) : terom@50: """ terom@50: Just uses format_txt, but wraps in

terom@50:         """
terom@50:         
terom@50:         # open pre
terom@50:         yield "
"
terom@50:         
terom@50:         # format using IrssiTextFormatter
terom@65:         for line in self.format_txt(lines, full_timestamps) :
terom@50:             # escape HTML
terom@69:             line = xml.sax.saxutils.escape(line)
terom@69: 
terom@69:             # process links
terom@69:             line = self._process_links(line)
terom@69: 
terom@69:             # yield
terom@69:             yield line
terom@50: 
terom@50:         # close pre
terom@50:         yield "
" terom@50: terom@50: # define formatters by name terom@50: FORMATTERS = { terom@50: 'irssi': IrssiFormatter, terom@50: } terom@50: terom@50: def by_name (name) : terom@50: """ terom@64: Lookup and return a class LogFormatter by name terom@50: """ terom@50: terom@64: return FORMATTERS[name] terom@50: