logger.py revision c7f1593f9af3ea1b9264b37628c36f3a70e1749a
1#!/usr/bin/python 2# 3# Copyright 2010 Google Inc. All Rights Reserved. 4 5from itertools import chain 6import gzip 7import logging 8import logging.handlers 9import time 10import traceback 11 12 13def SetUpRootLogger(filename=None, level=None, display_flags={}): 14 console_handler = logging.StreamHandler() 15 console_handler.setFormatter(CustomFormatter(AnsiColorCoder(), display_flags)) 16 logging.root.addHandler(console_handler) 17 18 if filename: 19 file_handler = logging.handlers.RotatingFileHandler( 20 filename, maxBytes=10*1024*1024, backupCount=9, delay=True) 21 file_handler.setFormatter(CustomFormatter(NullColorCoder(), display_flags)) 22 logging.root.addHandler(file_handler) 23 24 if level: 25 logging.root.setLevel(level) 26 27 28class NullColorCoder(object): 29 def __call__(self, *args): 30 return '' 31 32 33class AnsiColorCoder(object): 34 CODES = {'reset': (0, ), 35 'bold': (1, 22), 36 'italics': (3, 23), 37 'underline': (4, 24), 38 'inverse': (7, 27), 39 'strikethrough': (9, 29), 40 'black': (30, 40), 41 'red': (31, 41), 42 'green': (32, 42), 43 'yellow': (33, 43), 44 'blue': (34, 44), 45 'magenta': (35, 45), 46 'cyan': (36, 46), 47 'white': (37, 47)} 48 49 def __call__(self, *args): 50 codes = [] 51 52 for arg in args: 53 if arg.startswith('bg-') or arg.startswith('no-'): 54 codes.append(self.CODES[arg[3:]][1]) 55 else: 56 codes.append(self.CODES[arg][0]) 57 58 return '\033[%sm' % ';'.join(map(str, codes)) 59 60 61class CustomFormatter(logging.Formatter): 62 COLORS = {'DEBUG': ('white',), 63 'INFO': ('green',), 64 'WARN': ('yellow', 'bold'), 65 'ERROR': ('red', 'bold'), 66 'CRIT': ('red', 'inverse', 'bold')} 67 68 def __init__(self, coder, display_flags={}): 69 items = [] 70 71 if display_flags.get('datetime', True): 72 items.append('%(asctime)s') 73 if display_flags.get('level', True): 74 items.append('%(levelname)s') 75 if display_flags.get('name', True): 76 items.append(coder('cyan') + '[%(threadName)s:%(name)s]' + coder('reset')) 77 items.append('%(prefix)s%(message)s') 78 79 logging.Formatter.__init__(self, fmt=' '.join(items)) 80 81 self._coder = coder 82 83 def formatTime(self, record): 84 ct = self.converter(record.created) 85 t = time.strftime("%Y-%m-%d %H:%M:%S", ct) 86 return "%s.%02d" % (t, record.msecs / 10) 87 88 def formatLevelName(self, record): 89 if record.levelname in ['WARNING', 'CRITICAL']: 90 levelname = record.levelname[:4] 91 else: 92 levelname = record.levelname 93 94 return ''.join([self._coder(*self.COLORS[levelname]), levelname, 95 self._coder('reset')]) 96 97 def formatMessagePrefix(self, record): 98 try: 99 return ' %s%s:%s ' % ( 100 self._coder('black', 'bold'), record.prefix, self._coder('reset')) 101 except AttributeError: 102 return '' 103 104 def format(self, record): 105 if record.exc_info: 106 if not record.exc_text: 107 record.exc_text = self.formatException(record.exc_info) 108 else: 109 record.exc_text = '' 110 111 fmt = record.__dict__.copy() 112 fmt.update({'levelname': self.formatLevelName(record), 113 'asctime': self.formatTime(record), 114 'prefix': self.formatMessagePrefix(record)}) 115 116 s = [] 117 118 for line in chain(record.getMessage().splitlines(), 119 record.exc_text.splitlines()): 120 fmt['message'] = line 121 122 s.append(self._fmt % fmt) 123 124 return '\n'.join(s) 125 126 127class CompressedFileHandler(logging.FileHandler): 128 def _open(self): 129 return gzip.open(self.baseFilename + '.gz', self.mode, 9) 130 131 132def HandleUncaughtExceptions(fun): 133 """Catches all exceptions that would go outside decorated fun scope.""" 134 135 def _Interceptor(*args, **kwargs): 136 try: 137 return fun(*args, **kwargs) 138 except StandardError: 139 logging.exception("Uncaught exception:") 140 141 return _Interceptor 142