add systemctl and ufw parsers, support for varying degrees of severity
[logparse.git] / logparse / formatting.py
index acdba9fc5ab1d3efb871f14488c6ba1a18c4d6c4..00ea9e45f663d71836bcc1a336d0eb0c4e3c2c08 100644 (file)
@@ -144,6 +144,8 @@ class PlaintextOutput(Output):
         """
 
         self.append(PlaintextBox(content=section.title, double=False, fullwidth=False, vpadding=0, hpadding=" ").draw())
+        if section.period and section.period.unique:
+            self.append("\n(since {0})".format(section.period.startdate.strftime(DATEFMT + " " + TIMEFMT)))
         self.append('\n'*2)
         for data in section.data:
             self.append(self._fmt_data(data.subtitle, data.items))
@@ -216,14 +218,35 @@ class HtmlOutput(Output):
         """
 
         if not self._embedded:
-            self._embedded = mail.mailprep(self.content, css)
+            self._embedded = mail.mailprep(re.sub(".*" + re.escape(VARSUBST['css']) + ".*\n", "", self.content), css)
         return self._embedded
 
+    def write_embedded(self, destination = ""):
+        """
+        Write contents to file with inline CSS tags
+        """
+
+        logger.debug("Writing HTML with embedded styles to " + destination)
+        if not self._embedded:
+            logger.warning("Call to write_embedded before embed_css - \
+                    embedding stylesheets immediately")
+            self.embed_css(config.prefs.get("html", "css"))
+        if destination == "":
+            destination = self.destination
+        if destination == "":
+            logger.warning("No destination path provided")
+            return 1
+        with open(destination, 'w') as f:
+            f.write(self._embedded)
+            logger.info("Written output to {}".format(destination))
+
+
     def append_header(self, template):
         """
         Insert variables into header template file and append HTML tags
         """
 
+        self.headertemplate = template
         headercontent = Template(open(template, 'r').read())
         self.append(headercontent.safe_substitute(VARSUBST))
         self.append(opentag('div', id='main'))
@@ -244,8 +267,10 @@ class HtmlOutput(Output):
 
         self.append(opentag('div', 1, section.title, 'section'))
         self.append(self._gen_title(section.title))
+        if section.period and section.period.unique:
+            self.append(self._fmt_period(section.period))
         for data in section.data:
-            self.append(self._fmt_data(data.subtitle, data.items))
+            self.append(self._fmt_data(data.subtitle, data.items, data.severity))
         for table in section.tables:
             self.append(table.draw_html())
         self.append(closetag('div', 1))
@@ -261,10 +286,11 @@ class HtmlOutput(Output):
         logger.debug("Writing title for " + title)
         return tag('h2', False, title)
 
-    def _fmt_data(self, subtitle, data = None):
+    def _fmt_data(self, subtitle, data=None, severity=0):
         """
         Format the properties of a data object into usable HTML tags.
-        Subtitle is required, data is not. If only subtitle is supplied or subtitle + one data item, a single line will be printed.
+        Subtitle is required, data is not. If only subtitle is supplied or
+        subtitle + one data item, a single line will be printed.
         """
 
         if (subtitle == ""):
@@ -273,15 +299,15 @@ class HtmlOutput(Output):
 
         if (data == None or len(data) == 0):
             logger.debug("No data provided.. just printing subtitle")
-            return tag('p', False, subtitle)
+            return tag('p', False, subtitle, cl="severity-" + str(severity))
         else:
             logger.debug("Received data " + str(data))
             subtitle += ':'
             if (len(data) == 1):
-                return tag('p', False, subtitle + ' ' + data[0])
+                return tag('p', False, subtitle + ' ' + data[0], cl="severity-" + str(severity))
             else:
                 output = ""
-                output += tag('p', False, subtitle)
+                output += tag('p', False, subtitle, cl="severity-" + str(severity))
                 output += opentag('ul', 1)
                 coderegex = re.compile('`(.*)`')
                 for datum in data:
@@ -292,16 +318,41 @@ class HtmlOutput(Output):
                 output += closetag('ul', True)
                 return output
 
+    def _fmt_period(self, period):
+        output = ''
+        output += opentag('span', cl='period')
+        output += "since " + period.startdate.strftime(DATEFMT + " " + TIMEFMT)
+        output += closetag('span')
+        return output
+
+    def print_stdout_embedded(self, lines=False):
+        """
+        Echo the version with embedded style tags to the console
+        """
+
+        if self._embedded == "":
+            self.embed_css(config.prefs.get("html", "css"))
+        print()
+        if lines:
+            line = PlaintextLine(linewidth=config.prefs.getint("plain", "linewidth"), double=True)
+            print(line.draw())
+        print(self._embedded)
+        if lines:
+            print(line.draw())
+        print()
+
 
 class Section:
     """
-    Each parser should output a Section() which contains the title and returned data.
+    Each parser should output a Section() which contains the title, returned
+    data, and applicable time period.
     """
 
-    def __init__(self, title):
+    def __init__(self, title, period=None):
         self.title = title
         self.data = []
         self.tables = []
+        self.period = util.LogPeriod(self.title)
 
     def append_data(self, data):
         self.data.append(data)
@@ -313,17 +364,21 @@ class Section:
 class Data:
     """
     Each section (parser) can have one or more Data() objects which are
-    essentially glorified lists.
+    essentially glorified lists with titles (`self.subtitle`).
     """
     
-    def __init__(self, subtitle="", items=[]):
+    def __init__(self, subtitle="", items=[], severity=0):
         """
         Initialise variables. No parameters are enforced upon initialisation,
-        but at least the subtitle is required for valid output.
+        but at least the subtitle is required for valid output. Severity refers
+        to the importance of the data (integer from 0 to 5). e.g. a failed
+        system should have severity 5 and will be formatted appropriately by
+        the Output object.
         """
 
         self.subtitle = subtitle
         self.items = items 
+        self.severity = severity
 
     def truncl(self, limit):      # truncate list
         """
@@ -336,6 +391,7 @@ class Data:
                 return 0
             self.items = self.items[:limit]
             self.items.append("+ {0} more".format(str(more)))
+        return self
 
     def orderbyfreq(self):
         """
@@ -345,6 +401,7 @@ class Data:
 
         unsorted = list(self.items)
         self.items = ["{0} ({1})".format(y, unsorted.count(y)) for y in sorted(set(unsorted), key = lambda x: -unsorted.count(x))]
+        return self
 
 
 class Table(object):
@@ -593,14 +650,16 @@ def plural(noun, quantity, print_quantity=True):
     Return "1 noun" or "n nouns"
     """
 
-    if print_quantity:
-        if (quantity == 1):
+    if (quantity == 1):
+        if print_quantity:
             return(str(quantity) + " " + noun)
         else:
-            return(str(quantity) + " " + noun + "s")
-    else:
-        if (quantity == 1):
             return noun
+    else:
+        if noun.endswith("s"):
+            noun += "e"
+        if print_quantity:
+            return(str(quantity) + " " + noun + "s")
         else:
             return noun + "s"
 
@@ -669,4 +728,3 @@ def tag(tag, block=False, content="", id=None, cl=None, style=None):
     o = opentag(tag, block, id, cl, style)
     c = closetag(tag, block)
     return o + content + c
-