hitl_tester.modules.logger
This module provides logging capabilities to both a file (defined in the yaml config) and the console. It may be used by any other module.
(c) 2020-2024 TurnAround Factor, Inc.
CUI DISTRIBUTION CONTROL Controlled by: DLA J68 R&D SBIP CUI Category: Small Business Research and Technology Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS POC: GOV SBIP Program Manager Denise Price, 571-767-0111 Distribution authorized to U.S. Government Agencies only, to protect information not owned by the U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317, Fort Belvoir, VA 22060-6221
SBIR DATA RIGHTS Contract No.:SP4701-23-C-0083 Contractor Name: TurnAround Factor, Inc. Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005 Expiration of SBIR Data Rights Period: September 24, 2029 The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause contained in the above identified contract. No restrictions apply after the expiration date shown above. Any reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce the markings.
1""" 2This module provides logging capabilities to both a file (defined in the yaml config) and the console. 3It may be used by any other module. 4 5(c) 2020-2024 TurnAround Factor, Inc. 6 7CUI DISTRIBUTION CONTROL 8Controlled by: DLA J68 R&D SBIP 9CUI Category: Small Business Research and Technology 10Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS 11POC: GOV SBIP Program Manager Denise Price, 571-767-0111 12Distribution authorized to U.S. Government Agencies only, to protect information not owned by the 13U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that 14it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests 15for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317, 16Fort Belvoir, VA 22060-6221 17 18SBIR DATA RIGHTS 19Contract No.:SP4701-23-C-0083 20Contractor Name: TurnAround Factor, Inc. 21Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005 22Expiration of SBIR Data Rights Period: September 24, 2029 23The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer 24software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights 25in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause 26contained in the above identified contract. No restrictions apply after the expiration date shown above. Any 27reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce 28the markings. 29""" 30 31from __future__ import annotations 32 33import logging 34import re 35from pathlib import Path 36from typing import ClassVar 37 38from typing_extensions import Self 39 40 41class Logger: 42 """Provides logging features for the HITL.""" 43 44 instance: ClassVar[Self | None] = None 45 46 def __new__(cls): 47 """Make Logger a singleton.""" 48 if cls.instance is None: 49 cls.instance = super().__new__(cls) 50 return cls.instance 51 52 def __init__(self, log_level: int = logging.DEBUG): 53 """Create the logger object.""" 54 self.logger = logging.getLogger("HITL") 55 self.logger.setLevel(log_level) 56 self._html_result_text: list[str] = [] 57 self.log_file: Path = Path() 58 59 # Silence other modules 60 for logger_name in ("pyvisa", "asyncio", "can"): 61 logging.getLogger(logger_name).setLevel(logging.WARNING) 62 63 def use_file_mode(self, report_filename: Path, log_level: int = logging.DEBUG): 64 """Switch to a file based log""" 65 self.logger.setLevel(log_level) 66 67 class NoANSIFormatter(logging.Formatter): 68 """Strips ANSI escape codes from the log message.""" 69 70 def format(self, record): 71 """Return logger message with terminal escapes removed.""" 72 record.msg = re.sub(r"\x1b\[[0-9;]*m", "", record.msg) 73 return super().format(record) 74 75 formatter = NoANSIFormatter( 76 "%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%m/%d/%Y %I:%M:%S%p" 77 ) 78 79 # Setup the date part of the filename 80 self.log_file = Path(f"{report_filename}.txt") 81 82 file_log_handler = logging.FileHandler(self.log_file) 83 file_log_handler.setLevel(self.logger.level) 84 file_log_handler.setFormatter(formatter) 85 self.logger.addHandler(file_log_handler) 86 self.write_debug_to_report(f"Log Filename: {self.log_file}") 87 88 # TODO(JA): is it better to access self.logger.info (and friends) directly? 89 90 def write_result_to_report(self, result, exc_info=None): 91 """Provide an interface to allow test cases to write test results to the test report.""" 92 self.logger.info(f"Test Result - {result}", exc_info=exc_info) 93 94 def write_debug_to_report(self, entry, exc_info=None): 95 """Detailed information, useful only when a problem is being diagnosed.""" 96 self.logger.debug(str(entry), exc_info=exc_info) 97 98 def write_info_to_report(self, entry, exc_info=None): 99 """This is used to confirm that everything is working as it should.""" 100 self.logger.info(str(entry), exc_info=exc_info) 101 102 def write_warning_to_report(self, entry, exc_info=None): 103 """Something unexpected has happened or some problem is about to happen in the near future.""" 104 self.logger.warning(str(entry), exc_info=exc_info) 105 106 def write_error_to_report(self, entry, exc_info=None): 107 """An error has occurred. The software was unable to perform some function.""" 108 self.logger.error(str(entry), exc_info=exc_info) 109 110 def write_critical_to_report(self, entry, exc_info=None): 111 """A serious error has occurred. We may shut down or not be able to continue running properly.""" 112 self.logger.critical(str(entry), exc_info=exc_info) 113 114 def write_result_to_html_report(self, text: str): 115 """Add text to the result section of the current test.""" 116 self.logger.info(text) 117 self._html_result_text.append(text) 118 119 def write_warning_to_html_report(self, text: str): 120 """Add warning text to the result section of the current test.""" 121 self.logger.warning(text) 122 self._html_result_text.append(f'<font color="#ca6500">⚠️ {text} </font>') 123 124 def write_failure_to_html_report(self, text: str): 125 """Add failing error text to the result section of the current test.""" 126 self.logger.warning(text) 127 self._html_result_text.append(f'<font color="#990000">{text}</font>') 128 129 @property 130 def html_result_text(self) -> list[str]: 131 """Fetch and clear html text.""" 132 result = self._html_result_text[:] 133 del self._html_result_text[:] 134 return result 135 136 137logger = Logger()
42class Logger: 43 """Provides logging features for the HITL.""" 44 45 instance: ClassVar[Self | None] = None 46 47 def __new__(cls): 48 """Make Logger a singleton.""" 49 if cls.instance is None: 50 cls.instance = super().__new__(cls) 51 return cls.instance 52 53 def __init__(self, log_level: int = logging.DEBUG): 54 """Create the logger object.""" 55 self.logger = logging.getLogger("HITL") 56 self.logger.setLevel(log_level) 57 self._html_result_text: list[str] = [] 58 self.log_file: Path = Path() 59 60 # Silence other modules 61 for logger_name in ("pyvisa", "asyncio", "can"): 62 logging.getLogger(logger_name).setLevel(logging.WARNING) 63 64 def use_file_mode(self, report_filename: Path, log_level: int = logging.DEBUG): 65 """Switch to a file based log""" 66 self.logger.setLevel(log_level) 67 68 class NoANSIFormatter(logging.Formatter): 69 """Strips ANSI escape codes from the log message.""" 70 71 def format(self, record): 72 """Return logger message with terminal escapes removed.""" 73 record.msg = re.sub(r"\x1b\[[0-9;]*m", "", record.msg) 74 return super().format(record) 75 76 formatter = NoANSIFormatter( 77 "%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%m/%d/%Y %I:%M:%S%p" 78 ) 79 80 # Setup the date part of the filename 81 self.log_file = Path(f"{report_filename}.txt") 82 83 file_log_handler = logging.FileHandler(self.log_file) 84 file_log_handler.setLevel(self.logger.level) 85 file_log_handler.setFormatter(formatter) 86 self.logger.addHandler(file_log_handler) 87 self.write_debug_to_report(f"Log Filename: {self.log_file}") 88 89 # TODO(JA): is it better to access self.logger.info (and friends) directly? 90 91 def write_result_to_report(self, result, exc_info=None): 92 """Provide an interface to allow test cases to write test results to the test report.""" 93 self.logger.info(f"Test Result - {result}", exc_info=exc_info) 94 95 def write_debug_to_report(self, entry, exc_info=None): 96 """Detailed information, useful only when a problem is being diagnosed.""" 97 self.logger.debug(str(entry), exc_info=exc_info) 98 99 def write_info_to_report(self, entry, exc_info=None): 100 """This is used to confirm that everything is working as it should.""" 101 self.logger.info(str(entry), exc_info=exc_info) 102 103 def write_warning_to_report(self, entry, exc_info=None): 104 """Something unexpected has happened or some problem is about to happen in the near future.""" 105 self.logger.warning(str(entry), exc_info=exc_info) 106 107 def write_error_to_report(self, entry, exc_info=None): 108 """An error has occurred. The software was unable to perform some function.""" 109 self.logger.error(str(entry), exc_info=exc_info) 110 111 def write_critical_to_report(self, entry, exc_info=None): 112 """A serious error has occurred. We may shut down or not be able to continue running properly.""" 113 self.logger.critical(str(entry), exc_info=exc_info) 114 115 def write_result_to_html_report(self, text: str): 116 """Add text to the result section of the current test.""" 117 self.logger.info(text) 118 self._html_result_text.append(text) 119 120 def write_warning_to_html_report(self, text: str): 121 """Add warning text to the result section of the current test.""" 122 self.logger.warning(text) 123 self._html_result_text.append(f'<font color="#ca6500">⚠️ {text} </font>') 124 125 def write_failure_to_html_report(self, text: str): 126 """Add failing error text to the result section of the current test.""" 127 self.logger.warning(text) 128 self._html_result_text.append(f'<font color="#990000">{text}</font>') 129 130 @property 131 def html_result_text(self) -> list[str]: 132 """Fetch and clear html text.""" 133 result = self._html_result_text[:] 134 del self._html_result_text[:] 135 return result
Provides logging features for the HITL.
53 def __init__(self, log_level: int = logging.DEBUG): 54 """Create the logger object.""" 55 self.logger = logging.getLogger("HITL") 56 self.logger.setLevel(log_level) 57 self._html_result_text: list[str] = [] 58 self.log_file: Path = Path() 59 60 # Silence other modules 61 for logger_name in ("pyvisa", "asyncio", "can"): 62 logging.getLogger(logger_name).setLevel(logging.WARNING)
Create the logger object.
64 def use_file_mode(self, report_filename: Path, log_level: int = logging.DEBUG): 65 """Switch to a file based log""" 66 self.logger.setLevel(log_level) 67 68 class NoANSIFormatter(logging.Formatter): 69 """Strips ANSI escape codes from the log message.""" 70 71 def format(self, record): 72 """Return logger message with terminal escapes removed.""" 73 record.msg = re.sub(r"\x1b\[[0-9;]*m", "", record.msg) 74 return super().format(record) 75 76 formatter = NoANSIFormatter( 77 "%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%m/%d/%Y %I:%M:%S%p" 78 ) 79 80 # Setup the date part of the filename 81 self.log_file = Path(f"{report_filename}.txt") 82 83 file_log_handler = logging.FileHandler(self.log_file) 84 file_log_handler.setLevel(self.logger.level) 85 file_log_handler.setFormatter(formatter) 86 self.logger.addHandler(file_log_handler) 87 self.write_debug_to_report(f"Log Filename: {self.log_file}")
Switch to a file based log
91 def write_result_to_report(self, result, exc_info=None): 92 """Provide an interface to allow test cases to write test results to the test report.""" 93 self.logger.info(f"Test Result - {result}", exc_info=exc_info)
Provide an interface to allow test cases to write test results to the test report.
95 def write_debug_to_report(self, entry, exc_info=None): 96 """Detailed information, useful only when a problem is being diagnosed.""" 97 self.logger.debug(str(entry), exc_info=exc_info)
Detailed information, useful only when a problem is being diagnosed.
99 def write_info_to_report(self, entry, exc_info=None): 100 """This is used to confirm that everything is working as it should.""" 101 self.logger.info(str(entry), exc_info=exc_info)
This is used to confirm that everything is working as it should.
103 def write_warning_to_report(self, entry, exc_info=None): 104 """Something unexpected has happened or some problem is about to happen in the near future.""" 105 self.logger.warning(str(entry), exc_info=exc_info)
Something unexpected has happened or some problem is about to happen in the near future.
107 def write_error_to_report(self, entry, exc_info=None): 108 """An error has occurred. The software was unable to perform some function.""" 109 self.logger.error(str(entry), exc_info=exc_info)
An error has occurred. The software was unable to perform some function.
111 def write_critical_to_report(self, entry, exc_info=None): 112 """A serious error has occurred. We may shut down or not be able to continue running properly.""" 113 self.logger.critical(str(entry), exc_info=exc_info)
A serious error has occurred. We may shut down or not be able to continue running properly.
115 def write_result_to_html_report(self, text: str): 116 """Add text to the result section of the current test.""" 117 self.logger.info(text) 118 self._html_result_text.append(text)
Add text to the result section of the current test.
120 def write_warning_to_html_report(self, text: str): 121 """Add warning text to the result section of the current test.""" 122 self.logger.warning(text) 123 self._html_result_text.append(f'<font color="#ca6500">⚠️ {text} </font>')
Add warning text to the result section of the current test.
125 def write_failure_to_html_report(self, text: str): 126 """Add failing error text to the result section of the current test.""" 127 self.logger.warning(text) 128 self._html_result_text.append(f'<font color="#990000">{text}</font>')
Add failing error text to the result section of the current test.