1# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 2# 3# SPDX-License-Identifier: MPL-2.0 4# 5# This Source Code Form is subject to the terms of the Mozilla Public 6# License, v. 2.0. If a copy of the MPL was not distributed with this 7# file, you can obtain one at https://mozilla.org/MPL/2.0/. 8# 9# See the COPYRIGHT file distributed with this work for additional 10# information regarding copyright ownership. 11 12import logging 13from pathlib import Path 14from typing import Dict, Optional 15 16 17CONFTEST_LOGGER = logging.getLogger("conftest") 18LOG_FORMAT = "%(asctime)s %(levelname)7s:%(name)s %(message)s" 19 20LOGGERS = { 21 "conftest": None, 22 "module": None, 23 "test": None, 24} # type: Dict[str, Optional[logging.Logger]] 25 26 27def init_conftest_logger(): 28 """ 29 This initializes the conftest logger which is used for pytest setup 30 and configuration before tests are executed -- aka any logging in this 31 file that is _not_ module-specific. 32 """ 33 LOGGERS["conftest"] = logging.getLogger("conftest") 34 LOGGERS["conftest"].setLevel(logging.DEBUG) 35 file_handler = logging.FileHandler("pytest.conftest.log.txt") 36 file_handler.setFormatter(logging.Formatter(LOG_FORMAT)) 37 LOGGERS["conftest"].addHandler(file_handler) 38 39 40def avoid_duplicated_logs(): 41 """ 42 Remove direct root logger output to file descriptors. 43 This default is causing duplicates because all our messages go through 44 regular logging as well and are thus displayed twice. 45 """ 46 todel = [] 47 for handler in logging.root.handlers: 48 if handler.__class__ == logging.StreamHandler: 49 # Beware: As for pytest 7.2.2, LiveLogging and LogCapture 50 # handlers inherit from logging.StreamHandler 51 todel.append(handler) 52 for handler in todel: 53 logging.root.handlers.remove(handler) 54 55 56init_conftest_logger() 57avoid_duplicated_logs() 58 59 60def init_module_logger(system_test_name: str, testdir: Path): 61 logger = logging.getLogger(system_test_name) 62 logger.handlers.clear() 63 logger.setLevel(logging.DEBUG) 64 handler = logging.FileHandler(testdir / "pytest.log.txt", mode="w") 65 handler.setFormatter(logging.Formatter(LOG_FORMAT)) 66 logger.addHandler(handler) 67 LOGGERS["module"] = logger 68 69 70def deinit_module_logger(): 71 for handler in LOGGERS["module"].handlers: 72 handler.flush() 73 handler.close() 74 LOGGERS["module"] = None 75 76 77def init_test_logger(system_test_name: str, test_name: str): 78 LOGGERS["test"] = logging.getLogger(f"{system_test_name}.{test_name}") 79 80 81def deinit_test_logger(): 82 LOGGERS["test"] = None 83 84 85def log(lvl: int, msg: str, *args, **kwargs): 86 """Log message with the most-specific logger currently available.""" 87 logger = LOGGERS["test"] 88 if logger is None: 89 logger = LOGGERS["module"] 90 if logger is None: 91 logger = LOGGERS["conftest"] 92 assert logger is not None 93 logger.log(lvl, msg, *args, **kwargs) 94 95 96def debug(msg: str, *args, **kwargs): 97 log(logging.DEBUG, msg, *args, **kwargs) 98 99 100def info(msg: str, *args, **kwargs): 101 log(logging.INFO, msg, *args, **kwargs) 102 103 104def warning(msg: str, *args, **kwargs): 105 log(logging.WARNING, msg, *args, **kwargs) 106 107 108def error(msg: str, *args, **kwargs): 109 log(logging.ERROR, msg, *args, **kwargs) 110 111 112def critical(msg: str, *args, **kwargs): 113 log(logging.CRITICAL, msg, *args, **kwargs) 114