Files
prod-end-2026/backend/app/utils/business_logger.py
T
2026-03-17 18:32:44 +03:00

104 lines
3.2 KiB
Python

from __future__ import annotations
import logging
import os
from typing import Any
from app.utils.log_context import get_log_context
business_logger = logging.getLogger("app.business")
EVENT_SCHEMA_VERSION = "1.0"
SERVICE_NAME = os.getenv("APP_SERVICE_NAME", "backend-api")
def _derive_event_group(event: str) -> tuple[str, str | None]:
normalized = (event or "").strip().lower()
if normalized.startswith("auth_"):
return "auth", None
if normalized.startswith("action_") or normalized.startswith("actions_"):
return "actions", None
if (
normalized.startswith("capability_")
or normalized.startswith("capabilities_")
or normalized.startswith("composite_capability_")
):
return "capabilities", None
if normalized.startswith("pipeline_prompt_"):
return "pipelines", "prompt"
if normalized.startswith("pipeline_run_"):
return "pipelines", "run"
if normalized.startswith("pipeline_dialog_"):
return "pipelines", "dialog"
if normalized.startswith("pipeline_") or normalized.startswith("pipelines_"):
return "pipelines", None
if normalized.startswith("execution_run_"):
return "executions", "run"
if normalized.startswith("execution_step_"):
return "executions", "step"
if normalized.startswith("execution_") or normalized.startswith("executions_"):
return "executions", None
if normalized.startswith("user_") or normalized.startswith("users_"):
return "users", None
return "other", None
def _derive_event_outcome(event: str) -> str:
normalized = (event or "").strip().lower()
for suffix, outcome in (
("_succeeded", "success"),
("_created", "success"),
("_updated", "success"),
("_deleted", "success"),
("_processed", "success"),
("_finished", "success"),
("_failed", "failure"),
("_rejected", "failure"),
("_blocked", "failure"),
("_started", "progress"),
("_queued", "progress"),
("_received", "progress"),
("_listed", "read"),
("_fetched", "read"),
("_viewed", "read"),
):
if normalized.endswith(suffix):
return outcome
return "unknown"
def log_business_event(event: str, **fields: Any) -> None:
safe_fields: dict[str, Any] = {
"event": event,
"event_schema_version": EVENT_SCHEMA_VERSION,
"service_name": SERVICE_NAME,
}
event_group, event_subgroup = _derive_event_group(event)
event_outcome = _derive_event_outcome(event)
if "event_group" not in fields:
safe_fields["event_group"] = event_group
if event_subgroup is not None and "event_subgroup" not in fields:
safe_fields["event_subgroup"] = event_subgroup
if "event_outcome" not in fields:
safe_fields["event_outcome"] = event_outcome
for key, value in get_log_context().items():
if key not in fields:
safe_fields[key] = value
for key, value in fields.items():
if isinstance(value, (str, int, float, bool)) or value is None:
safe_fields[key] = value
else:
safe_fields[key] = str(value)
business_logger.info(event, extra=safe_fields)