Skip to content

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 in error);
  • 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, the sum/avg/min/max/count aggregations with by()/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;
  • node appears in /api/v1/labels and in label_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