Source code for potpyri.utils.logger
"""Logging utilities for the POTPyRI pipeline.
Provides a colored console and file logger with UTC timestamps for pipeline
steps. Authors: Kerry Paterson, Charlie Kilpatrick.
"""
from potpyri._version import __version__
import logging
import time
import datetime
import os
# Define some colors for different log levels
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
#The background is set with 40 plus the number of the color, and the foreground with 30
#These are the sequences need to get colored ouput
RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"
[docs]
def get_log(log_dir):
"""Create and return a ColoredLogger writing to the given directory.
Log filename is generated as log_YYYYMMDD_HHMMSS.log (UTC).
Parameters
----------
log_dir : str
Directory path for the log file.
Returns
-------
ColoredLogger
Logger instance with stream and file handlers.
"""
datestr = datetime.datetime.now(datetime.UTC).strftime('%Y%m%d_%H%M%S')
base_logname = f'log_{datestr}.log'
log_filename = os.path.join(log_dir, base_logname)
log = ColoredLogger(log_filename)
return(log)
# Dictionary of colors for different log levels
COLORS = {
'WARNING': YELLOW,
'INFO': GREEN,
'DEBUG': BLUE,
'CRITICAL': YELLOW,
'ERROR': RED
}
[docs]
class ColoredLogger(logging.Logger):
"""Logger that writes to both console (colored) and a file (UTC, no color)."""
ST_FMT = "[$BOLD%(filename)s::%(lineno)d$RESET] [%(levelname)s] %(message)s"
F_FMT = "[$BOLD%(asctime)s::%(filename)s::%(lineno)d$RESET] [%(levelname)s] %(message)s"
STREAM_FORMAT = formatter_message(ST_FMT, True)
FILE_FORMAT = formatter_message(F_FMT, False)
def __init__(self, filename):
"""Create logger with stream (colored) and file (UTC) handlers.
Parameters
----------
filename : str
Path to the log file. File is opened in 'w+' mode.
"""
# Set logging level
logging.Logger.__init__(self, None, logging.INFO)
# Create a stream handler
streamhandler = logging.StreamHandler()
streamhandler.setFormatter(ColoredFormatter(self.STREAM_FORMAT, use_color=True))
# Now a file handler
filehandler = logging.FileHandler(filename, mode='w+')
filehandler.setLevel(logging.INFO)
file_formatter = ColoredFormatter(self.FILE_FORMAT, use_color=False)
file_formatter.converter = time.gmtime #convert time in logger to UTC
filehandler.setFormatter(ColoredFormatter(self.FILE_FORMAT, use_color=False))
# Add both handlers
self.addHandler(streamhandler)
self.addHandler(filehandler)
return
[docs]
def close(self):
"""Close and remove all handlers.
Call when the logger is no longer needed (e.g., end of test or script)
to avoid ResourceWarnings from unclosed file handles.
"""
for handler in self.handlers[:]:
handler.close()
self.removeHandler(handler)
[docs]
def shutdown(self):
"""Shut down the logging system and flush all handlers."""
logging.shutdown()
return