# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Martin Lemay
import logging
from typing import Any, Union
from typing_extensions import Self
__doc__ = """
Logger module manages logging tools.
Code was modified from <https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output>
"""
[docs]
class CountWarningHandler( logging.Handler ):
"""Create an handler to count the warnings logged."""
def __init__( self: Self ) -> None:
"""Init the handler."""
super().__init__()
self.warningCount = 0
[docs]
def emit( self: Self, record: logging.LogRecord ) -> None:
"""Count all the warnings logged.
Args:
record (logging.LogRecord): Record.
"""
if record.levelno == logging.WARNING:
self.warningCount += 1
# Add the convenience method for the logger
[docs]
def results( self: logging.Logger, message: str, *args: Any, **kws: Any ) -> None:
"""Logs a message with the custom 'RESULTS' severity level.
This level is designed for summary information that should always be
visible, regardless of the logger's verbosity setting.
Args:
self (logging.Logger): The logger instance.
message (str): The primary log message, with optional format specifiers (e.g., "Found %d issues.").
*args: The arguments to be substituted into the `message` string.
**kws: Keyword arguments for special functionality.
"""
if self.isEnabledFor( RESULTS_LEVEL_NUM ):
self._log( RESULTS_LEVEL_NUM, message, args, **kws )
# Define logging levels at the module level so they are available for the Formatter class
DEBUG: int = logging.DEBUG
INFO: int = logging.INFO
WARNING: int = logging.WARNING
ERROR: int = logging.ERROR
CRITICAL: int = logging.CRITICAL
# Define and register the new level for check results
RESULTS_LEVEL_NUM: int = 60
RESULTS_LEVEL_NAME: str = "RESULTS"
logging.addLevelName( RESULTS_LEVEL_NUM, RESULTS_LEVEL_NAME )
logging.Logger.results = results # type: ignore[attr-defined]
# types redefinition to import logging.* from this module
Logger = logging.Logger # logger type
[docs]
def getLogger( title: str, use_color: bool = False ) -> Logger:
"""Return the Logger with pre-defined configuration.
This function is now idempotent regarding handler addition.
Calling it multiple times with the same title will return the same
logger instance without adding more handlers if one is already present.
Example:
.. code-block:: python
# module import
import Logger
# logger instantiation
logger :Logger.Logger = Logger.getLogger("My application")
# logger use
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
logger.results("results message")
Args:
title (str): Name of the logger.
use_color (bool): If True, configure the logger to output with color.
Defaults to False.
Returns:
Logger: logger
"""
logger = logging.getLogger( title )
# Only configure the logger (add handlers, set level) if it hasn't been configured before.
if not logger.hasHandlers(): # More Pythonic way to check if logger.handlers is empty
logger.setLevel( INFO ) # Set the desired default level for this logger
# Create and add the stream handler
ch = logging.StreamHandler()
ch.setFormatter( CustomLoggerFormatter( use_color ) ) # Use your custom formatter
logger.addHandler( ch )
# Optional: Prevent messages from propagating to the root logger's handlers
logger.propagate = False
# If you need to ensure a certain level is set every time getLogger is called,
# even if handlers were already present, you can set the level outside the 'if' block.
# However, typically, setLevel is part of the initial handler configuration.
# logger.setLevel(INFO) # Uncomment if you need to enforce level on every call
return logger