Source code for metobs_toolkit.backend_collection.loggingmodule
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Logging utilities for the MetObs Toolkit.
Provides functions to add file and stream handlers to the toolkit logger.
Created on Fri Aug 2 14:23:30 2024
@author: thoverga
"""
import os
import logging
from datetime import datetime
from os import PathLike
from metobs_toolkit.settings_collection.settings import Settings
logger = logging.getLogger("<metobs_toolkit>")
[docs]
def add_FileHandler(
filepath: str | PathLike,
setlvl: str = Settings.get("log_level"),
logformat: str = Settings.get("log_format"),
clearlog: bool = True,
) -> None:
"""
Add a FileHandler to the Toolkit logger.
A FileHandler directs the logs generated by the `metobs_toolkit` to a file.
Parameters
----------
filepath : str | PathLike
Path of the target log file.
setlvl : {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}, optional
The logger level for the FileHandler. See
https://docs.python.org/3/library/logging.html#levels for more details.
The default is "DEBUG".
logformat : str, optional
The format of the log messages. The default is
"LOG:: %(levelname)s - %(message)s".
clearlog : bool, optional
If True, the `trglogfile` is cleared before adding the FileHandler.
The default is True.
Returns
-------
None
Notes
-----
This function checks existing handlers to avoid duplicate FileHandlers for the same file.
If a FileHandler already exists for the same file path with the same or more restrictive
(higher) log level, no new handler is added. Log levels in order of restrictiveness:
DEBUG < INFO < WARNING < ERROR < CRITICAL.
"""
# Lazy import to avoid circular dependency
from metobs_toolkit.io_collection.filewriters import fmt_output_filepath
filepath = fmt_output_filepath(
filepath=filepath, default_filename="metobs_toolkit.log", overwrite=clearlog
)
rootlog = logging.getLogger("<metobs_toolkit>")
# Convert target level to numeric value for comparison
target_level = getattr(logging, setlvl.upper())
# Normalize the target file path for comparison
target_path = os.path.abspath(filepath)
# Check if FileHandler already exists for same file at same or higher level
for handler in rootlog.handlers:
if isinstance(handler, logging.FileHandler):
# Compare normalized file paths
existing_path = os.path.abspath(handler.baseFilename)
if existing_path == target_path and handler.level <= target_level:
rootlog.debug(
f"FileHandler already exists for file '{filepath}' "
f"at level {logging.getLevelName(handler.level)} "
f"(<= {setlvl.upper()}). No new FileHandler added."
)
return None
# Create the Handler for logging data to a file - will be inherited for children
file_handler = logging.FileHandler(filename=filepath)
file_handler.setLevel(setlvl.upper()) # set handler level
# Create a Formatter for formatting the log messages
file_logger_formatter = logging.Formatter(logformat)
file_handler.setFormatter(file_logger_formatter)
# Add the Handler to the Logger
rootlog.addHandler(file_handler)
# Ensure the root logger level allows messages to reach the handler
rootlog.setLevel(logging.DEBUG)
rootlog.debug(f"FileHandler set at {datetime.now()}")
[docs]
def add_StreamHandler(
setlvl: str = Settings.get("log_level"), logformat: str = Settings.get("log_format")
) -> None:
"""
Add a StreamHandler to the Toolkit logger.
A StreamHandler directs the logs generated by the `metobs_toolkit` to `sys.stderr`.
Parameters
----------
setlvl : str, optional
The logger level for the StreamHandler. Must be one of ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"].
See https://docs.python.org/3/library/logging.html#levels for more details. Default is "DEBUG".
logformat : str, optional
The format string for log messages. Default is "LOG:: %(levelname)s - %(message)s".
Returns
-------
None
Notes
-----
This function checks existing handlers to avoid duplicate StreamHandlers.
If a StreamHandler already exists with the same or more restrictive (higher) log level,
no new handler is added. Log levels in order of restrictiveness:
DEBUG < INFO < WARNING < ERROR < CRITICAL.
"""
# Get rootlogger
rootlog = logging.getLogger("<metobs_toolkit>")
rootlog.setLevel(logging.DEBUG) # set rootlogger on debug
# Convert target level to numeric value for comparison
target_level = getattr(logging, setlvl.upper())
# Check if StreamHandler already exists at same or higher level
for handler in rootlog.handlers:
if isinstance(handler, logging.StreamHandler) and not isinstance(
handler, logging.FileHandler
):
if handler.level <= target_level:
rootlog.debug(
f"StreamHandler already exists at level {logging.getLevelName(handler.level)} "
f"(<= {setlvl.upper()}). No new StreamHandler added."
)
return None
# Create StreamHandler
streamhandler = logging.StreamHandler()
streamhandler.setLevel(setlvl.upper())
stream_logger_formatter = logging.Formatter(logformat)
streamhandler.setFormatter(stream_logger_formatter)
# Add the Handler to the Logger
rootlog.addHandler(streamhandler)
rootlog.info(f"StreamHandler set at {datetime.now()}")