from __future__ import annotations import asyncio from collections.abc import Mapping from typing import Any from app.core.logging import PlanetLoggerAdapter, sanitize_log_value from app.core.request_context import get_request_id from app.services.persistent_logs import record_system_log LEVEL_METHODS = { "debug": "debug_event", "info": "info_event", "warning": "warning_event", "error": "error_event", } def normalize_business_level(level: str | None) -> str: normalized = str(level or "info").strip().lower() if normalized in {"warn", "warning"}: return "warning" if normalized in {"err", "error", "critical", "fatal"}: return "error" if normalized == "debug": return "debug" return "info" def build_business_context( context: Mapping[str, Any] | None = None, **fields: Any, ) -> dict[str, Any]: payload = dict(context or {}) for key, value in fields.items(): if value is not None: payload[key] = value return sanitize_log_value(payload) async def emit_business_log( logger: PlanetLoggerAdapter, *, event: str, message: str, category: str, level: str = "info", source: str = "backend", service: str | None = None, module: str | None = None, request_id: str | None = None, user_id: int | None = None, context: Mapping[str, Any] | None = None, ) -> None: normalized_level = normalize_business_level(level) safe_context = build_business_context(context) log_method = getattr(logger, LEVEL_METHODS[normalized_level]) log_method(message, event=event, context=safe_context) await record_system_log( source=source, level=normalized_level, message=message, service=service, module=module, event=event, request_id=request_id or get_request_id(), user_id=user_id, category=category, context=safe_context, ) def emit_business_log_background( logger: PlanetLoggerAdapter, *, event: str, message: str, category: str, level: str = "info", source: str = "backend", service: str | None = None, module: str | None = None, request_id: str | None = None, user_id: int | None = None, context: Mapping[str, Any] | None = None, ) -> None: normalized_level = normalize_business_level(level) safe_context = build_business_context(context) log_method = getattr(logger, LEVEL_METHODS[normalized_level]) log_method(message, event=event, context=safe_context) try: loop = asyncio.get_running_loop() except RuntimeError: return loop.create_task( record_system_log( source=source, level=normalized_level, message=message, service=service, module=module, event=event, request_id=request_id or get_request_id(), user_id=user_id, category=category, context=safe_context, ) ) def exception_context(exc: BaseException, context: Mapping[str, Any] | None = None) -> dict[str, Any]: return build_business_context( context, error_type=type(exc).__name__, error=str(exc), )