1import logging 2from threading import Thread 3 4from mozlog import commandline, stdadapter, set_default_logger 5from mozlog.structuredlog import StructuredLogger, log_levels 6 7 8def setup(args, defaults, formatter_defaults=None): 9 logger = args.pop('log', None) 10 if logger: 11 set_default_logger(logger) 12 StructuredLogger._logger_states["web-platform-tests"] = logger._state 13 else: 14 logger = commandline.setup_logging("web-platform-tests", args, defaults, 15 formatter_defaults=formatter_defaults) 16 setup_stdlib_logger() 17 18 for name in list(args.keys()): 19 if name.startswith("log_"): 20 args.pop(name) 21 22 return logger 23 24 25def setup_stdlib_logger(): 26 logging.root.handlers = [] 27 logging.root = stdadapter.std_logging_adapter(logging.root) 28 29 30class LogLevelRewriter(object): 31 """Filter that replaces log messages at specified levels with messages 32 at a different level. 33 34 This can be used to e.g. downgrade log messages from ERROR to WARNING 35 in some component where ERRORs are not critical. 36 37 :param inner: Handler to use for messages that pass this filter 38 :param from_levels: List of levels which should be affected 39 :param to_level: Log level to set for the affected messages 40 """ 41 def __init__(self, inner, from_levels, to_level): 42 self.inner = inner 43 self.from_levels = [item.upper() for item in from_levels] 44 self.to_level = to_level.upper() 45 46 def __call__(self, data): 47 if data["action"] == "log" and data["level"].upper() in self.from_levels: 48 data = data.copy() 49 data["level"] = self.to_level 50 return self.inner(data) 51 52 53class LoggedAboveLevelHandler(object): 54 """Filter that records whether any log message above a certain level has been 55 seen. 56 57 :param min_level: Minimum level to record as a str (e.g., "CRITICAL") 58 59 """ 60 def __init__(self, min_level): 61 self.min_level = log_levels[min_level.upper()] 62 self.has_log = False 63 64 def __call__(self, data): 65 if (data["action"] == "log" and 66 not self.has_log and 67 log_levels[data["level"]] <= self.min_level): 68 self.has_log = True 69 70 71class QueueHandler(logging.Handler): 72 def __init__(self, queue, level=logging.NOTSET): 73 self.queue = queue 74 logging.Handler.__init__(self, level=level) 75 76 def createLock(self): 77 # The queue provides its own locking 78 self.lock = None 79 80 def emit(self, record): 81 msg = self.format(record) 82 data = {"action": "log", 83 "level": record.levelname, 84 "thread": record.threadName, 85 "pid": record.process, 86 "source": self.name, 87 "message": msg} 88 self.queue.put(data) 89 90 91class LogQueueThread(Thread): 92 """Thread for handling log messages from a queue""" 93 def __init__(self, queue, logger): 94 self.queue = queue 95 self.logger = logger 96 super().__init__(name="Thread-Log") 97 98 def run(self): 99 while True: 100 try: 101 data = self.queue.get() 102 except (EOFError, IOError): 103 break 104 if data is None: 105 # A None message is used to shut down the logging thread 106 break 107 self.logger.log_raw(data) 108