1import logging, os, sys, time 2 3# set up a simple catchall configuration for use during import time. some code 4# may log messages at import time and we don't want those to get completely 5# thrown away. we'll clear this out when actual configuration takes place. 6logging.basicConfig(level=logging.DEBUG) 7 8class AllowBelowSeverity(logging.Filter): 9 """ 10 Allows only records less severe than a given level (the opposite of what 11 the normal logging level filtering does. 12 """ 13 def __init__(self, level): 14 self.level = level 15 16 17 def filter(self, record): 18 return record.levelno < self.level 19 20 21class LoggingConfig(object): 22 global_level = logging.DEBUG 23 stdout_level = logging.INFO 24 stderr_level = logging.ERROR 25 26 FILE_FORMAT = ('%(asctime)s.%(msecs)03d %(levelname)-5.5s|%(module)18.18s:' 27 '%(lineno)4.4d| %(message)s') 28 29 file_formatter = logging.Formatter( 30 fmt=FILE_FORMAT, datefmt='%m/%d %H:%M:%S') 31 32 CONSOLE_FORMAT = '%(asctime)s %(levelname)-5.5s| %(message)s' 33 34 console_formatter = logging.Formatter( 35 fmt=CONSOLE_FORMAT, datefmt='%H:%M:%S') 36 37 def __init__(self, use_console=True): 38 self.logger = logging.getLogger() 39 self.global_level = logging.DEBUG 40 self.use_console = use_console 41 42 43 @classmethod 44 def get_timestamped_log_name(cls, base_name): 45 return '%s.log.%s' % (base_name, time.strftime('%Y-%m-%d-%H.%M.%S')) 46 47 48 @classmethod 49 def get_autotest_root(cls): 50 common_lib_dir = os.path.dirname(__file__) 51 return os.path.abspath(os.path.join(common_lib_dir, '..', '..')) 52 53 54 @classmethod 55 def get_server_log_dir(cls): 56 return os.path.join(cls.get_autotest_root(), 'logs') 57 58 59 def add_stream_handler(self, stream, level=logging.DEBUG, datefmt=None): 60 handler = logging.StreamHandler(stream) 61 handler.setLevel(level) 62 formatter = self.console_formatter 63 if datefmt: 64 formatter = logging.Formatter(fmt=self.CONSOLE_FORMAT, 65 datefmt=datefmt) 66 handler.setFormatter(formatter) 67 self.logger.addHandler(handler) 68 return handler 69 70 71 def add_console_handlers(self, datefmt=None): 72 stdout_handler = self.add_stream_handler( 73 sys.stdout, level=self.stdout_level, datefmt=datefmt) 74 # only pass records *below* STDERR_LEVEL to stdout, to avoid duplication 75 stdout_handler.addFilter(AllowBelowSeverity(self.stderr_level)) 76 77 self.add_stream_handler(sys.stderr, self.stderr_level, datefmt) 78 79 80 def add_file_handler(self, file_path, level=logging.DEBUG, log_dir=None, 81 datefmt=None): 82 if log_dir: 83 file_path = os.path.join(log_dir, file_path) 84 handler = logging.FileHandler(file_path) 85 handler.setLevel(level) 86 formatter = self.file_formatter 87 if datefmt: 88 formatter = logging.Formatter(fmt=self.FILE_FORMAT, datefmt=datefmt) 89 handler.setFormatter(formatter) 90 self.logger.addHandler(handler) 91 return handler 92 93 94 def _add_file_handlers_for_all_levels(self, log_dir, log_name): 95 for level in (logging.DEBUG, logging.INFO, logging.WARNING, 96 logging.ERROR): 97 file_name = '%s.%s' % (log_name, logging.getLevelName(level)) 98 self.add_file_handler(file_name, level=level, log_dir=log_dir) 99 100 101 def add_debug_file_handlers(self, log_dir, log_name=None): 102 raise NotImplementedError 103 104 105 def _clear_all_handlers(self): 106 for handler in list(self.logger.handlers): 107 self.logger.removeHandler(handler) 108 # Attempt to close the handler. If it's already closed a KeyError 109 # will be generated. http://bugs.python.org/issue8581 110 try: 111 handler.close() 112 except KeyError: 113 pass 114 115 116 def configure_logging(self, use_console=True, verbose=False, datefmt=None): 117 self._clear_all_handlers() # see comment at top of file 118 self.logger.setLevel(self.global_level) 119 120 if verbose: 121 self.stdout_level = logging.DEBUG 122 if use_console: 123 self.add_console_handlers(datefmt) 124 125 126class TestingConfig(LoggingConfig): 127 def add_stream_handler(self, *args, **kwargs): 128 pass 129 130 131 def add_file_handler(self, *args, **kwargs): 132 pass 133 134 135 def configure_logging(self, **kwargs): 136 pass 137