124 lines
3.5 KiB
Python
124 lines
3.5 KiB
Python
"""
|
|
Central logging setup for the application.
|
|
|
|
- Provides a unified logger via get_logger(__name__)
|
|
- Writes all logs to a central rotating log file (logs/app.log)
|
|
- Writes errors separately to logs/errors.log
|
|
- Automatically includes the module name in each log entry
|
|
- Supports standard logging levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
|
|
Usage:
|
|
from backend.managers.debug_logger import get_logger
|
|
logger = get_logger(__name__)
|
|
|
|
logger.info("Service started")
|
|
logger.debug("Debug details")
|
|
logger.error("Something went wrong")
|
|
|
|
try:
|
|
...
|
|
except Exception:
|
|
logger.exception("Unexpected error")
|
|
|
|
Logging levels (use consistently):
|
|
DEBUG: Detailed technical info for developers (variables, flow, internal state).
|
|
INFO: Normal application events (start/stop, successful operations, key milestones).
|
|
WARNING: Something unexpected happened, but the program continues normally.
|
|
ERROR: A specific operation failed, but the application is still running.
|
|
CRITICAL: A severe failure that may stop the application or make it unusable.
|
|
EXCEPTION: Same as ERROR, but used inside an `except` block and includes stacktrace
|
|
(via logger.exception()).
|
|
"""
|
|
|
|
import logging
|
|
from logging.handlers import RotatingFileHandler
|
|
from pathlib import Path
|
|
|
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
LOG_DIR = BASE_DIR / "logs"
|
|
LOG_DIR.mkdir(exist_ok=True)
|
|
|
|
class DebugLogger:
|
|
|
|
_initialized = False
|
|
_error_log: list[str] = []
|
|
|
|
@classmethod
|
|
def setup(cls):
|
|
# prevents multiple setup
|
|
if cls._initialized:
|
|
return
|
|
|
|
formatter = logging.Formatter(
|
|
"%(asctime)s [%(levelname)s] [%(name)s: Line %(lineno)d] %(message)s"
|
|
)
|
|
|
|
# Main log file
|
|
file_handler = RotatingFileHandler(
|
|
LOG_DIR / "app.log",
|
|
maxBytes=5_000_000,
|
|
backupCount=5,
|
|
encoding="utf-8"
|
|
)
|
|
|
|
file_handler.setFormatter(formatter)
|
|
|
|
# Separate Error-Log
|
|
error_handler = RotatingFileHandler(
|
|
LOG_DIR / "errors.log",
|
|
maxBytes=5_000_000,
|
|
backupCount=3,
|
|
encoding="utf-8"
|
|
)
|
|
|
|
error_handler.setLevel(logging.ERROR)
|
|
error_handler.setFormatter(formatter)
|
|
|
|
root_logger = logging.getLogger()
|
|
|
|
root_logger.setLevel(logging.DEBUG)
|
|
|
|
root_logger.addHandler(file_handler)
|
|
root_logger.addHandler(error_handler)
|
|
#root_logger.propagate = False
|
|
|
|
cls._initialized = True
|
|
|
|
@classmethod
|
|
def get_logger(cls, name: str):
|
|
cls.setup()
|
|
return logging.getLogger(name)
|
|
|
|
@classmethod
|
|
def log_error(cls, error_message: str) -> None:
|
|
cls.setup()
|
|
logging.error(error_message)
|
|
cls._error_log.append(error_message)
|
|
|
|
@classmethod
|
|
def get_errors(cls) -> list[str]:
|
|
return cls._error_log
|
|
|
|
@classmethod
|
|
def clear_errors(cls) -> None:
|
|
cls._error_log.clear()
|
|
|
|
@classmethod
|
|
def format_debug_output(cls, output: dict) -> str:
|
|
stdout = output.get("stdout", "").strip() or "(none)"
|
|
stderr = output.get("stderr", "").strip() or "(none)"
|
|
return_code = output.get("return_code", "")
|
|
return (
|
|
"=== Execution Result ===\n"
|
|
f"Exit Code: {return_code}\n"
|
|
"--- stdout ---\n"
|
|
f"{stdout}\n"
|
|
"--- stderr ---\n"
|
|
f"{stderr}"
|
|
)
|
|
|
|
|
|
# praktische shortcut function
|
|
def get_logger(name: str):
|
|
return DebugLogger.get_logger(name)
|