Source code for coseda.logging_utils
"""Logging helpers for consistent stdout formatting."""
from __future__ import annotations
import logging
import os
import sys
import datetime
from typing import Optional
_CONFIGURED = False
def _resolve_level(level: Optional[str]) -> int:
if level is None:
level = os.getenv("COSEDA_LOG_LEVEL", "INFO")
return getattr(logging, str(level).upper(), logging.INFO)
[docs]
def get_logger(name: Optional[str] = None) -> logging.Logger:
"""Return a configured logger for the given module or the root COSEDA logger."""
configure_logging()
if not name or name == "coseda":
return logging.getLogger("coseda")
if name.startswith("coseda."):
return logging.getLogger(name)
return logging.getLogger(f"coseda.{name}")
[docs]
def log_print(*args, sep: str = " ", end: str = "", file=None, flush: bool = False, level: Optional[str] = None):
"""
Drop-in replacement for print() that routes through the COSEDA logger.
`level` can be one of: "debug", "info", "warning", "error".
"""
msg = sep.join(str(arg) for arg in args)
if end:
msg = f"{msg}{end}"
msg = msg.rstrip("\n")
logger = get_logger()
if level:
log_fn = getattr(logger, level, logger.info)
log_fn(msg)
return
if file is sys.stderr:
logger.error(msg)
else:
logger.info(msg)
[docs]
def log_start(logfile_path, message):
"""
Print a message and append it (timestamped) to the logfile if a path is provided.
"""
log_print(message)
if logfile_path is not None:
with open(f"{logfile_path}", "a") as file:
file.write(f"{datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]}; {message}\n")
[docs]
def log_result(logfile_path, message, error):
"""
Log a success or failure message to stdout and, when available, to the logfile.
"""
if error is None:
log_print(message)
if logfile_path is not None:
with open(f"{logfile_path}", "a") as file:
file.write(f"{datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]}; {message}\n")
else:
log_print(error, file=sys.stderr)
log_print("+++ This error might already be fixed. Did you pull the latest version? +++", level="warning")
if logfile_path is not None:
with open(f"{logfile_path}", "a") as file:
file.write(f"{datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]}; Error: {error}\n")
[docs]
def shoutout(configfile_path):
"""Print which config file is being processed (tiny UX helper for loops)."""
log_print(f"working with {configfile_path}")