Statistics¶
Sapsan always collects the full set of counters — your license and your task only determine which channel you read them through. There are four channels:
| Channel | What it gives you | Availability |
|---|---|---|
| Admin API | an instant JSON snapshot: what is happening to the stream right now | everyone |
| Built-in Prometheus server | a ready-made Grafana datasource with a few hours of history — graphs without deploying a TSDB | everyone |
| Prometheus scrape | raw metrics for your own TSDB and Grafana | the "extended counters" option |
| Retroview | the vendor's cloud monitoring: months of history, trends, curated alerts, usage reconciliation | subscription |
The selection rule is simple:
- "What is happening right now?" — Admin API or the UI.
- "What happened over the last hours?" — the built-in Prometheus server.
- "What happened last week, and why did it break at night?" — Retroview.
- "I want my own Prometheus, Grafana, and alertmanager" — the extended counters option.
Stream stats in the Admin API¶
Stream list¶
GET /streamer/api-v4/streams/stats returns one element per stream — the channels page in the UI lives on this list, and it is also the right thing to poll from scripts.
| Field | What it means | How to use it |
|---|---|---|
status, status_since |
stream state and the moment it entered it | not running for longer than N minutes — alert |
bitrate_kbit |
the measured media bitrate of the active input, by frame timestamps | dropped visibly below nominal — the source degraded. This is not network throughput: a file can be read faster than real time |
source.url |
the input that is actually playing | differs from the first one in the config — the stream lives on a backup |
inputs[] |
runtime status of every configured input: url, status, check age checked_ago_ms, bitrate, error text |
see below |
dvr.from, dvr.to |
the archive window on this server | to lags behind current time — recording stopped; from stopped moving forward — cleanup is not working, disk overflow ahead |
bytes_in, bytes_out |
cumulative byte counters since the stream started | for rates and history use the built-in Prometheus server or Retroview: the snapshot resets on restart |
Statuses of inputs[] elements:
active— playing right now;ok— a backup, alive as of the last check;error— a backup with a recorded problem (text inerror);unchecked— a backup that has never been checked yet.
This answers the main failover question — "will we actually switch when the primary dies". Backups are checked periodically (recheck_secondary_inputs_interval, see backup sources); alert on error, on unchecked, and on a too-old checked_ago_ms — an unchecked backup cannot be considered working.
Single stream¶
GET /streamer/api-v4/streams/stats/{name} adds diagnostic sections to the same fields — they are absent from the list to keep the list cheap.
The input section — counters of the current input:
| Field | Why |
|---|---|
bytes, frames, bitrate_kbit |
whether the input delivers data at all, and how fast |
retries |
reconnects to the source: growing — the source or the network is unstable |
input_switches |
switches between inputs: growing — the input flaps, investigate the primary |
errors |
the aggregate input error counter: growing — there is a problem; the cause is in the extended counters or in Retroview |
num_sec_on_primary_input, num_sec_on_secondary_input |
seconds lived on the primary and on the backup: a notable share on the backup — the primary input is systematically bad |
num_sec_no_data |
seconds with no data at all |
errors_lost_packets, errors_broken_payload, errors_desync, … |
causal error breakdown — only with the "extended counters" option |
The dvr section in the single-stream response is extended with:
| Field | Why |
|---|---|
recorded_hours |
hours that actually contain data. Compare with the window width to - from: the difference is recording gaps (the stream was down, recording was off) |
bytes |
total size of the stream's archive on disk |
write.segments_written, write.segments_failed, write.segments_skipped |
fragment write counters: segments_failed growing — the archive is losing data right now, check the disk |
write.segments_written_slow / _delayed / _collapsed, write.segments_discontinuity |
write profiling — only with the "extended counters" option |
Note
The Admin API snapshot resets when the server restarts. It answers "what is happening now", but is suitable neither for bill reconciliation nor for analyzing yesterday's incident — that is what Retroview is for.
Built-in Prometheus server¶
Sapsan contains a built-in Prometheus server: the HTTP API /api/v1/query, /api/v1/query_range, /api/v1/series, /api/v1/labels, /api/v1/label/{name}/values, /api/v1/status/buildinfo on top of its own in-memory store. To Grafana it is a ready-made datasource: add a datasource of the Prometheus type, point it at the server URL — and build graphs without deploying a TSDB. The built-in UI graphs are powered by the same queries.
By-design limitations:
- history is a few hours, in memory; after a restart the store is empty. This is the "what is happening now" channel, not a metrics archive;
- PromQL is supported as a subset: selectors with label matchers,
rate/irate/increase, thesum/avg/min/max/countaggregations withby()/without(), arithmetic,offset. An unsupported construct returns an explicit error, not a distorted result; - the series set is the base one; deep series (per-PID, SRT, RTP) appear with the "extended counters" option.
Querying a single server¶
# stream ingest rate, bytes/s
rate(stream_input_bytes_total{stream="cam1"}[1m])
# input errors over the last 5 minutes
increase(stream_input_errors_total{stream="cam1"}[5m])
# is the archive being written with errors?
increase(stream_dvr_write_segments_failed_total{stream="cam1"}[10m])
# total server ingest traffic
sum(rate(stream_input_bytes_total[1m]))
# memory used by streams, by pipeline part
sum by (part) (stream_memory_bytes)
On a single server the series carry no node label.
Querying central¶
Central and the Sapsan nodes form a single complex: central collects statistics from all nodes, and its built-in Prometheus server serves the very same API. There is one difference, but it runs through everything — the node label:
- every series carries
node="node-name"— you can see which node produced it; - one stream can produce several series: a cluster stream is served in parts on different nodes;
nodeappears in/api/v1/labelsand inlabel_values(node)— that is what a Grafana dashboard variable with a node dropdown is made of.
# the stream as a whole, regardless of how many nodes it lives on
sum without (node) (rate(stream_input_bytes_total{stream="cam1"}[1m]))
# ingest traffic of the complex, broken down by node
sum by (node) (rate(stream_input_bytes_total[1m]))
# which nodes the stream lives on right now — check the node label of the result
stream_input_bytes_total{stream="cam1"}
The same dashboard works both against a single Sapsan and against central: aggregate with sum without (node) — on a single server the label does not exist, and the aggregation changes nothing.
Prometheus scrape: your own monitoring¶
The «extended counters» option
The scrape endpoints are only available with the extended counters license option. Without it, use the built-in Prometheus server and Retroview.
For installations with their own monitoring infrastructure Sapsan serves metrics in the Prometheus text format:
GET /streamer/api-v4/live-metrics— streams and inputs;GET /streamer/api-v4/sessions-metrics— playback sessions;GET /streamer/api-v4/dvr/metrics— archive disks and catalog;GET /streamer/api-v4/runtime/metrics— the process and the allocator.
Differences from the built-in Prometheus server: the history is stored on your side and is limited only by your TSDB retention; the full depth is available, including per-PID/SRT/RTP protocol metrics; alerting is your alertmanager. The counters are monotonic and survive observed restarts — this is also the channel for billing reconciliation on your side.
Extended counters¶
The "extended counters" option unlocks causal and protocol-level detail everywhere at once: in the input and dvr.write sections of the Admin API, in the series of the built-in Prometheus server, and in the Prometheus scrape. The Retroview telemetry always carries these counters regardless of the option — so root-cause analysis is available in Retroview even without it.
Input error causes¶
errors_lost_packets, errors_broken_payload, errors_desync, errors_ts_pat, errors_decoder_reset, errors_dropped_frames, resync and reorder counters.
Why: to move from "the input has errors" to "who is to blame". errors_lost_packets growing — the network loses packets; errors_broken_payload and errors_desync — the source corrupts the stream; errors_decoder_reset — codec-level stream problems. Without this detail you only see the aggregate errors.
MPEG-TS per PID¶
For every PID of the transport stream: errors_ts_cc (continuity counter violations — the primary transport loss indicator), errors_ts_tei, errors_ts_scrambled (descrambling failed — CAM/key problems), errors_ts_psi_checksum, broken PES, PCR jitter buckets, decoder buffer (HRD) underruns.
Why: this is broadcast-grade monitoring material. An alert on rate(errors_ts_cc) > 0 per PID catches transport degradation before the viewer sees it; PCR jitter and HRD show whether the stream will survive a hardware decoder.
SRT¶
RTT and its variance, losses and retransmits in both directions, too-late drops, buffer fill, keepalive timeouts.
Why: tuning latency to the real link and diagnosing "who loses — us or the remote side". Retransmits grow while RTT is stable — losses on the path; RTT jumps — the link is congested.
RTP/RTSP per channel¶
Lost packets, timestamp jumps and stalls, NACKs, buffer overflows — per RTP channel (video/audio) of a camera.
Why: camera fleet diagnostics — which camera is failing, before the artifact complaints arrive.
DVR write performance¶
segments_written_slow, segments_written_delayed, segments_written_collapsed, segments_discontinuity.
Why: an early "the disk cannot keep up" signal. The slow/delayed share grows under the same traffic — the storage is degrading; discontinuity means gaps you will later see in recorded_hours.
Retroview¶
Retroview is the vendor's cloud monitoring. Once a minute Sapsan sends telemetry — the full counter catalog, including all the extended detail — and Retroview keeps it for months. Nothing needs to be configured on the server: the channel works together with the license.
When to go to Retroview instead of the built-in Prometheus server:
- history and trends — traffic and stream count growth over months, capacity planning;
- postmortems — what happened to the stream at night: telemetry survives restarts and does not depend on server memory;
- usage reconciliation — the counters are sampled over time by an external system, so the bill can be verified even if the servers were restarted;
- root-cause analysis without the extended counters option — telemetry always carries the causal detail;
- curated alerts — product rules instead of homegrown ones.
Recipes¶
| Question | Where to look | Problem indicator |
|---|---|---|
| Is the stream alive? | status in the API; rate(stream_input_bytes_total[1m]) in the built-in Prometheus |
status is not running; ingest rate dropped to zero |
| Is the backup ready? | inputs[] in the stats list |
status is error or unchecked; checked_ago_ms far exceeds the check interval |
| Are we living on the backup? | source.url; num_sec_on_secondary_input |
the URL is not the first one from the config; seconds-on-backup keeps growing |
| Is the input degrading? | input.errors, input.retries; causes — extended counters or Retroview |
counters grow while the stream is alive |
| Is the archive being written? | dvr.to in the list; dvr.write.segments_failed in the single GET |
to lags behind wall-clock time; segments_failed grows |
| Are there gaps in the archive? | dvr.recorded_hours versus the to - from window |
recorded hours are notably fewer than the window width |
| Do the disks keep up? | segments_written_slow/_delayed (option); dvr_disk_free_bytes in the scrape |
the slow-write share grows; free space shrinks faster than expected |
| Is the server healthy? | runtime/metrics: process_rss_bytes; the built-in Prometheus: stream_memory_bytes |
memory grows monotonically without load growth |
| What happened at night? | Retroview | — |