Source code for rath.observability.logging
"""Redacted newline-delimited JSON records for operational logging."""
from __future__ import annotations
import json
import logging
from collections.abc import Callable, Mapping
from datetime import datetime, timezone
from rath.context import TraceContext
from rath.observability.redaction import redact
__all__ = ["StructuredLogger"]
[docs]
class StructuredLogger:
"""Emit stable JSON records without ever failing the application path."""
def __init__(self, sink: Callable[[str], None] | None = None) -> None:
self._sink = sink or logging.getLogger("openrath").info
[docs]
def emit(
self,
event: str,
*,
context: TraceContext | None = None,
fields: Mapping[str, object] | None = None,
) -> None:
record: dict[str, object] = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"event": event,
}
if context is not None:
record.update(
{
"trace_id": context.trace_id,
"span_id": context.span_id,
}
)
safe = redact(dict(fields or {}))
assert isinstance(safe, dict)
record.update(safe)
try:
self._sink(
json.dumps(
record,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
default=str,
)
)
except Exception:
return