Source code for package_maximizer.utils.logging_config
"""
Logging configuration for Package Maximizer.
Provides a single configured logger that supports both human-readable and
structured (JSON) output. Used across the CLI and web layers for consistent,
machine-parseable diagnostics.
"""
from __future__ import annotations
import json
import logging
import os
import sys
from typing import Any
_DEFAULT_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
[docs]
class JsonFormatter(logging.Formatter):
"""Formatter that emits one JSON object per log record."""
[docs]
def format(self, record: logging.LogRecord) -> str:
payload: dict[str, Any] = {
"ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
if record.exc_info:
payload["exc"] = self.formatException(record.exc_info)
return json.dumps(payload, ensure_ascii=False)
[docs]
def configure_logging(
level: str | int = "INFO",
*,
json_output: bool | None = None,
stream: Any = None,
) -> logging.Logger:
"""
Configure the root logger for Package Maximizer.
Args:
level: Log level name (e.g. ``"DEBUG"``) or numeric level.
json_output: Emit JSON lines instead of plain text. Auto-detected from
the ``PM_LOG_JSON`` environment variable when ``None``.
stream: Output stream (defaults to ``sys.stderr``).
Returns:
The configured root logger.
"""
if isinstance(level, str):
level = logging.getLevelName(level.upper())
if json_output is None:
json_output = os.environ.get("PM_LOG_JSON", "false").lower() == "true"
handler = logging.StreamHandler(stream or sys.stderr)
handler.setFormatter(
JsonFormatter() if json_output else logging.Formatter(_DEFAULT_FORMAT)
)
root = logging.getLogger()
# Replace existing handlers to avoid duplicate output.
root.handlers.clear()
root.addHandler(handler)
root.setLevel(level)
return root
[docs]
def get_logger(name: str) -> logging.Logger:
"""Return a named logger under the package hierarchy."""
return logging.getLogger(name)