118 lines
3.2 KiB
Python
118 lines
3.2 KiB
Python
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),
|
|
)
|