@demystify/platform-observability
v0.3.0
Published
Shared metrics + dashboard for Demystify services: canonical RED metric names, a prom-client registry with RED helpers, a framework-neutral /metrics handler, an offline HTML dashboard, and a span->metrics bridge. Leaf package.
Downloads
289
Readme
platform-observability — shared metrics + dashboard
@demystify/platform-observability (TS) · demystify-platform-observability (Py) · v0.2.0 · MIT
What it is / when to use it
The single source of truth for RED metrics across all six Demystify services.
Canonical dmstfy_* metric names, a metrics registry with RED helpers, a
framework-neutral /metrics handler, an offline HTML dashboard, and a bridge that
turns a module's existing span instrumentation into duration metrics for free.
Leaf package: depends only on prom-client (TS) / prometheus-client (Py). Zero
dependency on any module. Keyless, offline, deterministic.
Install standalone
pnpm add @demystify/platform-observability # TS
uv add demystify-platform-observability # PyCanonical metric names
Defined once, shared by both languages (METRIC_NAMES / fixture-locked):
| name | type | labels |
|---|---|---|
| dmstfy_http_requests_total | counter | method, route, status, tenant |
| dmstfy_http_request_duration_seconds | histogram | method, route, status, tenant |
| dmstfy_provider_calls_total | counter | provider, model, status |
| dmstfy_provider_call_duration_seconds | histogram | provider, model, status |
| dmstfy_provider_cost_micro_usd_total | counter | provider, model |
| dmstfy_queue_depth | gauge | queue |
| dmstfy_span_duration_seconds | histogram | span, module |
Durations are seconds; cost is integer µUSD (CONVENTIONS.md §2). Buckets:
[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10].
SDK usage
import {
createMetrics, mountMetricsHono, renderDashboardHtml, spanExporterToMetrics,
} from "@demystify/platform-observability";
const metrics = createMetrics();
metrics.httpRequest("GET", "/v1/chat", 200, tenantId, 12.4); // RED helper
metrics.providerCall("openai", "gpt-4o", "ok", 210, 1500); // count+dur+cost
metrics.queueDepth("gateway", 7);
mountMetricsHono(app, metrics); // GET /metrics (Prometheus text)
setSpanExporter(spanExporterToMetrics(metrics)); // spans -> duration histogram
const html = renderDashboardHtml(metrics.snapshot());from demystify_platform_observability import (
create_metrics, mount_metrics_fastapi, render_dashboard_html, span_exporter_to_metrics,
)
metrics = create_metrics()
metrics.http_request("GET", "/v1/chat", 200, tenant_id, 12.4)
metrics.provider_call("openai", "gpt-4o", "ok", 210, 1500)
metrics.queue_depth("gateway", 7)
mount_metrics_fastapi(app, metrics) # GET /metrics
set_span_exporter(span_exporter_to_metrics(metrics))
html = render_dashboard_html(metrics.snapshot())Mounting /metrics
- Hono:
mountMetricsHono(app, metrics, "/metrics"). - FastAPI:
mount_metrics_fastapi(app, metrics, "/metrics"). - Anything else:
metricsHandler(metrics)/metrics_handler(metrics)returns{status, contentType, body}— wire it to your framework.
Span → metrics bridge
spanExporterToMetrics(metrics) returns an object matching the Wave-7
SpanExporter shape (export(span) in TS; a Callable[[Span], None] in Py). Pass
it to a module's setSpanExporter(...) / set_span_exporter(...) and every
finished span becomes an observation on dmstfy_span_duration_seconds. Off by
default (no exporter registered = zero cost), exactly like §14.3.
Architecture & ports/adapters
| seam | implementation | notes |
|---|---|---|
| metric store | private prom-client / prometheus-client registry per Metrics | isolated; no global default registry |
| snapshot | deterministic in-memory tally | ordering-stable; powers dashboard + parity |
| exposition | metricsText() / metrics_text() | Prometheus v0.0.4 text |
Testing
make -C packages/platform-observability setup lint test build. Unit tests only
(offline). Parity: TS and Py replay the same fixtures/snapshot.json and must
produce byte-identical snapshots; metric names locked to fixtures/metric-names.json.
v1 scope
RED + cost + queue-depth + span-duration. No exemplars, no push-gateway, no histogram-quantile helpers (scrape + PromQL do that). Real Prometheus scrape is the standard opt-in path; nothing here requires it to run.
