Metrics export: what you get
Three metrics from the record to any OTLP collector you run, the rule that governs every label on them, and the metrics a monitoring team usually asks for that this deliberately refuses.
For whoever builds the dashboard a fleet is watched from.
After reading it you can say which series exist, which labels you may group by, and which metrics you will not get and why.
Before you start: nothing. Read this before you design a dashboard, not after.
Saphan can publish a small, deliberately narrow set of metrics from its own record to any OTLP/HTTP collector you run — Prometheus behind the OpenTelemetry Collector, Grafana Alloy, Datadog's OTLP intake, or anything else that speaks the protocol.
⛔ It never sends recorded content, never runs automatically, and is not on the run path. An export cannot slow a run down, because no run ever triggers one — see running an export.
Three metrics, and each one has a column behind it
The exporter reads and never computes. A metric exists only because a column in the record answers it directly.
| metric | unit | what backs it |
|---|---|---|
saphan.run.cost_usd | USD | the quote and the actual cost of a run, told apart by a saphan.cost.kind attribute |
saphan.review.rounds | count | the review round ordinal |
saphan.account.usage_pct | percent | a seat's session and week usage readings |
The rule that governs every label
⛔ An attribute is attached only when the record column carried a value. An empty column does not produce an empty label — it produces no label at all, and the series simply does not have that dimension.
That is deliberate, and the reason is worth reading before you build a query:
An attribute with an empty value is not a neutral default. An empty string here means this build did not answer, and a dashboard grouping on it invents a cohort that does not exist.
⇒ Do not assume a label is always present. Only the two marked always below are unconditional.
| metric | attribute | present |
|---|---|---|
saphan.run.cost_usd | saphan.cost.kind (quoted or actual) | always |
saphan.stream | when the record has it | |
gen_ai.system — the backend | when the record has it | |
gen_ai.request.model — the model | when the record has it | |
saphan.role | when the record has it | |
saphan.track | when the run has a track | |
saphan.pricing_ref | when the record has it | |
saphan.review.rounds | saphan.stream | when the record has it — and nothing else |
saphan.account.usage_pct | saphan.usage.window | always |
saphan.machine | when the record has it | |
saphan.seat.state | when the record has it |
⚠ Two of the cost labels use OpenTelemetry's own convention names rather than the saphan.
prefix — the backend is gen_ai.system and the model is gen_ai.request.model. A query
filtering on saphan.* alone will not see them.
⚠ No attribute is common to all three metrics. Seat usage carries no stream: it is a property of a seat, not of a run, so a dashboard cannot join the three on one dimension.
⚠ The name you give a collector rides the exporter's own health points, under an endpoint
attribute — not the record metrics above. Use it to tell two collectors apart in the exporter's
self-reporting; do not expect it as a filter on cost or usage. The collector's URL never reaches
the wire at all.
What is deliberately not exported
You will not find these, and each absence is a decision rather than a gap. Each is refused by name, and the export command prints the reason — so you never have to guess whether a missing metric is a bug.
| what a monitoring team usually asks for | why it is refused |
|---|---|
| a count of runs | no column counts runs |
| a gate decision | no column carries one as a value |
| a review verdict | no column, and no review table at all |
| egress dials and refusals | no column in the egress facts |
| a run's duration | ⛔ no elapsed-time column. The one column that looks like an answer is a configured cap — what a run was allowed, not what it took. Publishing a cap under a duration's name would be a lie, and deriving elapsed time would be a computation this exporter does not perform |
⇒ A metric with no row behind it is not registered. If you need one of the above, the answer is a new column in the record first — never a number derived at export time.
Privacy
Identifiers and fingerprints only. Prompts, transcripts, file contents, credentials and free text never leave the record. The collector's URL is not published on the wire either — only the name you choose for it.
⇒ Next: running an export — the three keys, the egress admission the address does not grant you, and the limit to read before you schedule anything.
Reading the document you are approving
The console's document viewer: what it shows, how fresh it is, and what is named rather than omitted.
Running a metrics export
Three keys, the egress admission the address does not grant you, what each exit status means, and the one limit to read before you schedule anything.