dataforge-playground / dataforge /observability.py
Praneshrajan15's picture
Deploy DataForge playground API
13fe504 verified
Raw
History Blame Contribute Delete
3.64 kB
"""Optional OpenTelemetry hooks for DataForge backend surfaces."""
from __future__ import annotations
import os
from collections.abc import Iterator
from contextlib import contextmanager, nullcontext
from importlib import import_module
from typing import Any, Literal
_SENSITIVE_ATTR_FRAGMENTS = ("authorization", "cookie", "token", "key", "secret", "password")
RepairTelemetryStage = Literal[
"detect",
"propose",
"safety_gate",
"smt_verify",
"transaction_create",
"transaction_apply",
"receipt",
"revert",
]
REPAIR_TELEMETRY_SPANS: dict[str, str] = {
"detect": "dataforge.repair.detect",
"propose": "dataforge.repair.propose",
"safety_gate": "dataforge.repair.safety_gate",
"smt_verify": "dataforge.repair.smt_verify",
"transaction_create": "dataforge.repair.transaction_create",
"transaction_apply": "dataforge.repair.transaction_apply",
"receipt": "dataforge.repair.receipt",
"revert": "dataforge.repair.revert",
}
REPAIR_TELEMETRY_STAGES = frozenset(REPAIR_TELEMETRY_SPANS)
def _otel_enabled() -> bool:
"""Return whether optional OpenTelemetry instrumentation is enabled."""
return os.environ.get("DATAFORGE_OTEL_ENABLED", "").strip().lower() in {
"1",
"true",
"yes",
"on",
}
def _safe_attrs(attributes: dict[str, Any]) -> dict[str, str | int | float | bool]:
"""Keep only scalar, non-sensitive telemetry attributes."""
safe: dict[str, str | int | float | bool] = {}
for key, value in attributes.items():
lowered = key.lower()
if any(fragment in lowered for fragment in _SENSITIVE_ATTR_FRAGMENTS):
continue
if lowered in {"row_values", "rows", "payload", "source_bytes", "csv"}:
continue
if isinstance(value, str | int | float | bool):
safe[key] = value
return safe
def configure_fastapi_observability(app: Any, *, service_name: str) -> bool:
"""Instrument a FastAPI app when OpenTelemetry is explicitly enabled."""
if not _otel_enabled():
return False
try:
fastapi_instrumentation = import_module("opentelemetry.instrumentation.fastapi")
trace_module = import_module("opentelemetry.trace")
except ImportError:
return False
app.state.dataforge_service_name = service_name
fastapi_instrumentation.FastAPIInstrumentor.instrument_app(
app,
tracer_provider=trace_module.get_tracer_provider(),
excluded_urls="/api/docs,/docs,/redoc,/openapi.json",
)
return True
def _span_name(stage: str) -> str:
"""Return the span name for a public repair telemetry stage."""
if stage in REPAIR_TELEMETRY_SPANS:
return REPAIR_TELEMETRY_SPANS[stage]
return stage
@contextmanager
def repair_stage_span(stage: RepairTelemetryStage | str, **attributes: Any) -> Iterator[None]:
"""Create a repair-stage span when OpenTelemetry is available."""
if not _otel_enabled():
with nullcontext():
yield
return
try:
trace_module = import_module("opentelemetry.trace")
except ImportError:
with nullcontext():
yield
return
tracer = trace_module.get_tracer("dataforge.repair")
with tracer.start_as_current_span(_span_name(stage)) as span:
if stage in REPAIR_TELEMETRY_SPANS:
span.set_attribute("dataforge.repair.stage", stage)
for key, value in _safe_attrs(attributes).items():
span.set_attribute(key, value)
yield