+"""
+This file sets up logging for the entire logparse package. Custom log formatter
+and handler classes are specified below and the logger is supplied to all
+subsequent modules, including parsers.
+"""
+
__version__ = '2.0'
__name__ = 'logparse'
+
+
+from copy import copy
+import logging
+import logging.handlers
+
+
+# Standard shell escape codes
+ESC = {
+ "reset": "\033[0m",
+ "color": "\033[1;%dm",
+ "bold": "\033[1m",
+ "underlined": "\033[4m"
+ }
+
+# Standard shell colour codes (30..39 are the foreground colors)
+DEFAULT = 39
+BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN = range(30, 37)
+
+# Map colours to log levels (used for level text only)
+COLORS = {
+ 10: BLUE, # debug
+ 20: DEFAULT, # info
+ 30: YELLOW, # warning
+ 40: RED, # error
+ 50: RED # critical
+ }
+
+# Template for formatting log messages
+FORMAT = ("{bold}%(module)-15s{reset} %(levelname)-18s %(message)s "
+ "({bold}%(filename)s{reset}:%(lineno)d)")
+
+
+class ColoredFormatter(logging.Formatter):
+ """
+ Custom implementation of a log formatter to apply standard shell colour
+ escape sequences depending on the log level. The original record is copied
+ so as to not interfere with subsequent handlings (i.e. the syslog handler).
+ """
+
+ def __init__(self, msg):
+ logging.Formatter.__init__(self, msg)
+
+ def format(self, record):
+ temprecord = copy(record)
+ levelname = temprecord.levelname
+ if temprecord.levelno in COLORS:
+ levelname_color = ESC["color"] % (COLORS[temprecord.levelno]) \
+ + levelname + ESC["reset"]
+ temprecord.levelname = levelname_color
+ temprecord.name = record.name.replace(__name__ + ".", "")
+ return logging.Formatter.format(self, temprecord)
+
+
+class ColoredLogger(logging.Logger):
+ """
+ Custom implementation of a logger object using the `ColoredFormatter` class
+ above. This class also includes a syslog handler to direct a minimal amount
+ of output to /dev/log.
+ """
+
+ message = FORMAT.format(**ESC)
+
+ def __init__(self, name):
+ """
+ Initialise the logger for the entire package. This is done here so that
+ the configuration is applied to all child modules. A syslog handler
+ is also initialised, with a min level of INFO so that journald doesn't
+ get spammed with debug messages..
+ """
+
+ logging.Logger.__init__(self, name)
+
+ color_formatter = ColoredFormatter(self.message)
+
+ syslog_handler = logging.handlers.SysLogHandler(address = '/dev/log')
+ syslog_handler.setLevel(logging.INFO)
+ syslog_handler.setFormatter(logging.Formatter(
+ fmt='{}[%(process)d]: (%(module)s) %(message)s'.format(__name__)))
+
+ console = logging.StreamHandler()
+ console.setFormatter(color_formatter)
+
+ self.addHandler(console)
+ self.addHandler(syslog_handler)
+ return
+
+
+# Initialise logger object
+logging.setLoggerClass(ColoredLogger)
+logger = logging.getLogger()