# Copyright 2010 Google Inc. All Rights Reserved. from itertools import chain import gzip import logging import logging.handlers import time import traceback def SetUpRootLogger(filename=None, level=None, display_flags={}): console_handler = logging.StreamHandler() console_handler.setFormatter(CustomFormatter(AnsiColorCoder(), display_flags)) logging.root.addHandler(console_handler) if filename: file_handler = logging.handlers.RotatingFileHandler( filename, maxBytes=10 * 1024 * 1024, backupCount=9, delay=True) file_handler.setFormatter(CustomFormatter(NullColorCoder(), display_flags)) logging.root.addHandler(file_handler) if level: logging.root.setLevel(level) class NullColorCoder(object): def __call__(self, *args): return '' class AnsiColorCoder(object): CODES = {'reset': (0,), 'bold': (1, 22), 'italics': (3, 23), 'underline': (4, 24), 'inverse': (7, 27), 'strikethrough': (9, 29), 'black': (30, 40), 'red': (31, 41), 'green': (32, 42), 'yellow': (33, 43), 'blue': (34, 44), 'magenta': (35, 45), 'cyan': (36, 46), 'white': (37, 47)} def __call__(self, *args): codes = [] for arg in args: if arg.startswith('bg-') or arg.startswith('no-'): codes.append(self.CODES[arg[3:]][1]) else: codes.append(self.CODES[arg][0]) return '\033[%sm' % ';'.join(map(str, codes)) class CustomFormatter(logging.Formatter): COLORS = {'DEBUG': ('white',), 'INFO': ('green',), 'WARN': ('yellow', 'bold'), 'ERROR': ('red', 'bold'), 'CRIT': ('red', 'inverse', 'bold')} def __init__(self, coder, display_flags={}): items = [] if display_flags.get('datetime', True): items.append('%(asctime)s') if display_flags.get('level', True): items.append('%(levelname)s') if display_flags.get('name', True): items.append(coder('cyan') + '[%(threadName)s:%(name)s]' + coder('reset')) items.append('%(prefix)s%(message)s') logging.Formatter.__init__(self, fmt=' '.join(items)) self._coder = coder def formatTime(self, record): ct = self.converter(record.created) t = time.strftime('%Y-%m-%d %H:%M:%S', ct) return '%s.%02d' % (t, record.msecs / 10) def formatLevelName(self, record): if record.levelname in ['WARNING', 'CRITICAL']: levelname = record.levelname[:4] else: levelname = record.levelname return ''.join([self._coder(*self.COLORS[levelname]), levelname, self._coder('reset')]) def formatMessagePrefix(self, record): try: return ' %s%s:%s ' % (self._coder('black', 'bold'), record.prefix, self._coder('reset')) except AttributeError: return '' def format(self, record): if record.exc_info: if not record.exc_text: record.exc_text = self.formatException(record.exc_info) else: record.exc_text = '' fmt = record.__dict__.copy() fmt.update({'levelname': self.formatLevelName(record), 'asctime': self.formatTime(record), 'prefix': self.formatMessagePrefix(record)}) s = [] for line in chain(record.getMessage().splitlines(), record.exc_text.splitlines()): fmt['message'] = line s.append(self._fmt % fmt) return '\n'.join(s) class CompressedFileHandler(logging.FileHandler): def _open(self): return gzip.open(self.baseFilename + '.gz', self.mode, 9) def HandleUncaughtExceptions(fun): """Catches all exceptions that would go outside decorated fun scope.""" def _Interceptor(*args, **kwargs): try: return fun(*args, **kwargs) except StandardError: logging.exception('Uncaught exception:') return _Interceptor