generated from coulomb/repo-seed
Add profile.sandbox-canary, HostSnapshot/inventory/stale schemas, SSH collectors, before/after provision deltas, telemetry export to State Hub and local JSON, default `sandboxer create` self-deploy, inspect/reap-stale CLI, runbook, and CoulombCore verification (26 tests pass).
64 lines
1.8 KiB
Python
64 lines
1.8 KiB
Python
"""Telemetry export sinks."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Protocol
|
|
|
|
import httpx
|
|
|
|
from sandboxer.lifecycle.state_hub import hub_url
|
|
from sandboxer.telemetry.models import IntrospectionReport
|
|
|
|
|
|
class TelemetrySink(Protocol):
|
|
"""Future export target (artifact-store, Prometheus, ClickHouse)."""
|
|
|
|
def publish(self, report: IntrospectionReport) -> None: ...
|
|
|
|
|
|
def telemetry_dir() -> Path:
|
|
base = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
|
|
path = base / "sandboxer" / "telemetry"
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
return path
|
|
|
|
|
|
def export_local_artifact(report: IntrospectionReport) -> Path:
|
|
path = telemetry_dir() / f"{report.sandbox_id}.json"
|
|
path.write_text(json.dumps(report.model_dump(mode="json"), indent=2, default=str))
|
|
return path
|
|
|
|
|
|
def export_state_hub(report: IntrospectionReport) -> dict | None:
|
|
if os.environ.get("SANDBOXER_NO_STATE_HUB", "").lower() in ("1", "true", "yes"):
|
|
return None
|
|
payload = {
|
|
"event_type": "note",
|
|
"summary": (
|
|
f"Telemetry {report.sandbox_id}: load Δ "
|
|
f"{report.provision_delta.load_1m_delta if report.provision_delta else 0}, "
|
|
f"stale={len(report.stale_candidates)}"
|
|
),
|
|
"author": "sandboxer",
|
|
"detail": report.model_dump(mode="json"),
|
|
}
|
|
try:
|
|
response = httpx.post(f"{hub_url()}/progress/", json=payload, timeout=10.0)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except httpx.HTTPError:
|
|
return None
|
|
|
|
|
|
def export_telemetry(report: IntrospectionReport) -> Path:
|
|
path = export_local_artifact(report)
|
|
export_state_hub(report)
|
|
return path
|
|
|
|
|
|
class NoopTelemetrySink:
|
|
def publish(self, report: IntrospectionReport) -> None:
|
|
export_telemetry(report) |