Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
99 lines
2.0 KiB
Python
99 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
from dataclasses import dataclass
|
|
from typing import Any, Iterable
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class QueryLogEvent:
|
|
"""Structured query log event.
|
|
|
|
Attributes:
|
|
query_name: Short name for the operation (e.g. "get_open_tasks").
|
|
sql: SQL statement with parameters redacted/normalized.
|
|
params: Parameters (redacted where needed).
|
|
"""
|
|
|
|
query_name: str
|
|
sql: str
|
|
params: tuple[Any, ...] | None
|
|
|
|
|
|
_SECRET_KEYS = re.compile(r"(pass(word)?|secret|token|key)", re.IGNORECASE)
|
|
|
|
|
|
def _redact_value(value: Any) -> Any:
|
|
"""Redact a single value for logging.
|
|
|
|
Args:
|
|
value: Value to redact.
|
|
|
|
Returns:
|
|
Redacted value.
|
|
"""
|
|
|
|
if value is None:
|
|
return None
|
|
if isinstance(value, (int, float, bool)):
|
|
return value
|
|
s = str(value)
|
|
if len(s) <= 4:
|
|
return "***"
|
|
return s[:2] + "***" + s[-2:]
|
|
|
|
|
|
def redact_params(params: Iterable[Any] | None) -> tuple[Any, ...] | None:
|
|
"""Redact query parameters for safe logging.
|
|
|
|
Args:
|
|
params: DB-API parameter sequence.
|
|
|
|
Returns:
|
|
Redacted parameter tuple.
|
|
"""
|
|
|
|
if params is None:
|
|
return None
|
|
return tuple(_redact_value(p) for p in params)
|
|
|
|
|
|
def normalize_sql(sql: str) -> str:
|
|
"""Normalize SQL for logging.
|
|
|
|
This removes excessive whitespace to keep log lines readable.
|
|
|
|
Args:
|
|
sql: SQL statement.
|
|
|
|
Returns:
|
|
Normalized SQL.
|
|
"""
|
|
|
|
return " ".join(sql.split())
|
|
|
|
|
|
def log_query(
|
|
*,
|
|
logger: logging.Logger,
|
|
query_name: str,
|
|
sql: str,
|
|
params: Iterable[Any] | None = None,
|
|
) -> None:
|
|
"""Emit a safe query log line.
|
|
|
|
Args:
|
|
logger: Logger to use.
|
|
query_name: Short name for the operation.
|
|
sql: SQL statement.
|
|
params: DB-API parameters.
|
|
"""
|
|
|
|
event = QueryLogEvent(
|
|
query_name=query_name,
|
|
sql=normalize_sql(sql),
|
|
params=redact_params(params),
|
|
)
|
|
logger.debug("DB query: %s", event)
|