import logging class GRCHandler(logging.Handler): # Inherit from logging.Handler ''' Custom log handler for GRC. Stores log entries to be viewed using the GRC debug window. ''' def __init__(self, maxLength=256): # run the regular Handler __init__ logging.Handler.__init__(self) # Our custom argument self.log = collections.deque(maxlen=maxLength) def emit(self, record): self.log.append(record) def getLogs(self, level): pass class ConsoleFormatter(logging.Formatter): ''' Custom log formatter that nicely truncates the log message and log levels - Verbose mode outputs: time, level, message, name, filename, and line number - Normal mode output varies based on terminal size: w < 80 - Level, Message (min length 40) 80 < w < 120 - Level, Message, File, Line (25) 120 < w - Level, Message, Name, File, Line - Color mode ouptuts the same variable sizes and uses the blessings module to add color ''' # TODO: Better handle multi line messages. Need to indent them or something def __init__(self, verbose=False, short_level=True): # Test for blessings formatter try: from blessings import Terminal self.terminal = Terminal() self.formatLevel = self.formatLevelColor except: self.terminal = None self.formatLevel = self.formatLevelPlain # Default to short self.formatLevelLength = self.formatLevelShort if not short_level: self.formatLevelLength = self.formatLevelLong # Setup the format function as a pointer to the correct formatting function # Determine size and mode # TODO: Need to update the sizes depending on short or long outputs import shutil size = shutil.get_terminal_size() width = max(40, size.columns - 10) if size.columns < 80: self.format = self.short self.width = width elif size.columns < 120: self.format = self.medium self.width = width - 30 elif size.columns >= 120: self.format = self.long self.width = width - 45 # Check if verbose mode. If so override other options if verbose: self.format = self.verbose # Normal log formmatters def short(self, record): message = self.formatMessage(record.msg, self.width) level = self.formatLevel(record.levelname) return "{0} -- {1}".format(level, message) def medium(self, record): message = self.formatMessage(record.msg, self.width) level = self.formatLevel(record.levelname) output = '{0} -- {1:<' + str(self.width) + '} ({2}:{3})' return output.format(level, message, record.filename, record.lineno) def long(self, record): message = self.formatMessage(record.msg, self.width) level = self.formatLevel(record.levelname) output = '{0} -- {1:<' + str(self.width) + '} {2} ({3}:{4})' return output.format(level, message, record.name, record.filename, record.lineno) ''' Verbose formatter ''' def verbose(self, record): # TODO: Still need to implement this pass ''' Level and message formatters ''' # Nicely format the levelname # Level name can be formated to either short or long, and also with color def formatLevelColor(self, levelname): term = self.terminal output = "{0}{1}{2}{3}" level = self.formatLevelLength(levelname) if levelname == "DEBUG": return output.format(term.blue, "", level, term.normal) elif levelname == "INFO": return output.format(term.green, "", level, term.normal) elif levelname == "WARNING": return output.format(term.yellow, "", level, term.normal) elif levelname == "ERROR": return output.format(term.red, term.bold, level, term.normal) elif levelname == "CRITICAL": return output.format(term.red, term.bold, level, term.normal) else: return output.format(term.blue, "", level, term.normal) def formatLevelPlain(self, levelname): ''' Format the level name without color. formatLevelLength points to the right function ''' return self.formatLevelLength(levelname) def formatLevelShort(self, levelname): return "[{0}]".format(levelname[0:1]) def formatLevelLong(self, levelname): output = "{0:<10}" if levelname in ["DEBUG", "INFO", "WARNING"]: return output.format("[{0}]".format(levelname.capitalize())) else: return output.format("[{0}]".format(levelname.upper())) def formatMessage(self, message, width): # First, strip out any newline for console output message = message.rstrip() if len(message) > width: return (message[:(width - 3)] + "...") return message