Estadísticas¶
Sapsan siempre recolecta el conjunto completo de contadores — la licencia y la tarea solo determinan por qué canal los lee usted. Hay cuatro canales:
| Canal | Qué le da | Disponibilidad |
|---|---|---|
| Admin API | una instantánea JSON del momento: qué le está pasando al stream ahora mismo | todos |
| Servidor Prometheus integrado | un datasource de Grafana listo para usar con unas horas de historia — gráficos sin desplegar una TSDB | todos |
| Scrape de Prometheus | métricas en bruto para su propia TSDB y Grafana | la opción de contadores extendidos |
| Retroview | la monitorización en la nube del fabricante: meses de historia, tendencias, alertas listas, conciliación de consumo | suscripción |
La regla de elección es sencilla:
- «¿Qué está pasando ahora?» — la Admin API o la UI.
- «¿Qué pasó en las últimas horas?» — el servidor Prometheus integrado.
- «¿Qué pasó la semana pasada y por qué se cayó de noche?» — Retroview.
- «Quiero mi propio Prometheus, Grafana y alertmanager» — la opción de contadores extendidos.
Estadísticas del stream en el Admin API¶
La lista de streams¶
GET /streamer/api-v4/streams/stats devuelve un elemento por stream — la página de streams de la UI vive de esta lista, y también es lo correcto para sondearla desde scripts.
| Campo | Qué significa | Cómo usarlo |
|---|---|---|
status, status_since |
el estado del stream y el momento en que entró en él | fuera de running durante más de N minutos — alerta |
bitrate_kbit |
el bitrate de medios medido de la entrada activa, por los timestamps de los fotogramas | caído visiblemente por debajo del nominal — la fuente se degradó. Esto no es el throughput de red: un archivo puede leerse más rápido que el tiempo real |
source.url |
la entrada que está reproduciendo realmente | difiere de la primera del config — el stream vive en una reserva |
inputs[] |
el estado de runtime de cada entrada configurada: url, status, antigüedad de la comprobación checked_ago_ms, bitrate, texto del error |
véase más abajo |
dvr.from, dvr.to |
la ventana del archivo en este servidor | to se retrasa respecto al tiempo actual — la grabación se detuvo; from dejó de avanzar — la limpieza no funciona, adelante un desbordamiento de disco |
bytes_in, bytes_out |
contadores acumulados de bytes desde el arranque del stream | para velocidades e historia use el servidor Prometheus integrado o Retroview: la instantánea se pone a cero con un reinicio |
Estados de los elementos de inputs[]:
active— reproduciendo ahora mismo;ok— una reserva, viva según la última comprobación;error— una reserva con un problema registrado (el texto, enerror);unchecked— una reserva que aún no se ha comprobado nunca.
Esto responde a la pregunta principal del failover: «¿conmutaremos de verdad cuando caiga la principal?». Las reservas se comprueban periódicamente (recheck_secondary_inputs_interval, véanse las fuentes de reserva); alerte sobre error, sobre unchecked y sobre un checked_ago_ms demasiado antiguo — una reserva sin comprobar no puede considerarse operativa.
Un solo stream¶
GET /streamer/api-v4/streams/stats/{name} añade a esos mismos campos secciones de diagnóstico — no están en la lista para que la lista siga siendo barata.
La sección input — contadores de la entrada actual:
| Campo | Para qué |
|---|---|
bytes, frames, bitrate_kbit |
si la entrada trae datos y a qué velocidad |
retries |
reconexiones a la fuente: crece — la fuente o la red son inestables |
input_switches |
conmutaciones entre entradas: crece — la entrada se cae y se recupera; investigue la principal |
errors |
el contador agregado de errores de entrada: crece — hay un problema; la causa, en los contadores extendidos o en Retroview |
num_sec_on_primary_input, num_sec_on_secondary_input |
segundos vividos en la principal y en la reserva: una proporción notable en la reserva — la entrada principal es sistemáticamente mala |
num_sec_no_data |
segundos sin datos en absoluto |
errors_lost_packets, errors_connection_closed, errors_http_request_error, … |
desglose de errores por causa — solo con la opción de contadores extendidos |
La sección dvr en la respuesta individual se amplía con:
| Campo | Para qué |
|---|---|
recorded_hours |
horas que realmente contienen datos. Compárelas con la anchura de la ventana to - from: la diferencia son huecos de grabación (el stream se cayó, la grabación estaba apagada) |
bytes |
tamaño total del archivo del stream en disco |
write.segments_written, write.segments_failed, write.segments_skipped |
contadores de escritura de fragmentos: segments_failed crece — el archivo está perdiendo datos ahora mismo, compruebe el disco |
write.segments_written_slow / _delayed / _collapsed, write.segments_discontinuity |
perfilado de la escritura — solo con la opción de contadores extendidos |
Note
La instantánea del Admin API se pone a cero cuando el servidor se reinicia. Responde a «qué está pasando ahora», pero no sirve ni para conciliar la factura ni para analizar el incidente de ayer — para eso está Retroview.
Estadísticas de pushes¶
La entrega saliente se cuenta aparte de la captura y por cada push: la clave de la estadística es el par «nombre del stream, nombre del push», el mismo que en la configuración. Un stream puede salir a cinco destinos, y «el stream envía 40 Mbit/s» no responde a si llega a alguno de ellos en concreto.
La opción «estadísticas de pushes»
La vista de pushes es una opción de licencia aparte. Corta solo los canales de salida: la sección pushes y los campos planos push_* del Admin API, las series stream_push_* del scrape del nodo y del servidor Prometheus integrado. La recolección en sí nunca se detiene, y los contadores siempre van a la telemetría de Retroview — el análisis en Retroview está disponible también sin la opción.
La sección pushes¶
GET /streamer/api-v4/streams/stats/{name} (y cada elemento de GET /streamer/api-v4/streams/stats) trae un objeto indexado por nombre de push:
"pushes": {
"cdn-main": {
"status": "running",
"proto": "udp",
"url": "udp://127.0.0.1:5500",
"opened_at": "2026-08-13T11:17:53Z",
"bitrate_kbit": 2113.4,
"bytes_total": 19053048,
"datagrams_total": 14478
},
"youtube": {
"status": "error",
"proto": "rtmp",
"url": "rtmp://198.51.100.7/live/secret-key",
"error": "rtmp connect failed: timeout after 5s",
"reconnects_total": 22
},
"backup-dc": {"status": "disabled", "proto": "srt", "url": "srt://203.0.113.10:9000"}
}
| Campo | Qué significa | Cómo usarlo |
|---|---|---|
status |
running, error o disabled |
fuera de running durante más de N minutos — alerta; disabled es normal, es un push que el operador apagó |
error |
la causa del estado error |
lo primero que hay que leer al diagnosticar: «connect failed», rechazo de publicación, handshake rechazado |
proto, url |
el transporte y el destino tal como los definió el operador | se ve adónde va realmente el stream |
opened_at |
el momento en que arrancó el pusher actual | se renueva en cada reconexión — el canal se cae y vuelve |
bitrate_kbit |
la velocidad de envío medida | claramente por debajo del bitrate del stream — el envío no da abasto |
errors_rate |
errores de envío por segundo | crece con la conexión viva — el receptor o el canal se están degradando |
bytes_total |
bytes enviados, acumulados | conciliar el volumen de salida por dirección |
errors_total |
errores de envío, acumulados | — |
reconnects_total |
resurrecciones de un pusher muerto | crece con status: running — el canal se corta y se recupera |
retransmitted_packets_total |
retransmisiones del transporte (SRT, RIST) | crecen — pérdidas en el camino hacia el receptor |
datagrams_total, frames_total |
las unidades enviadas: datagramas en los transportes de paquetes, fotogramas en RTMP | — |
Los campos en cero se omiten de la respuesta: un push RTMP no tiene datagramas, uno UDP no tiene ni fotogramas ni retransmisiones.
Junto a los contadores del stream, como campos de nivel superior de la misma respuesta, hay tres sumas sobre todos los pushes — push_bytes_total, push_errors_total, push_reconnects_total. Responden «cuánto envió el stream en total» sin recorrer el mapa.
Series de Prometheus¶
Cada contador de push es una serie propia con la etiqueta push:
| Serie | Etiquetas |
|---|---|
stream_push_bytes_total |
stream, push, proto |
stream_push_errors_total |
stream, push, proto |
stream_push_reconnects_total |
stream, push, proto |
stream_push_retransmitted_packets_total |
stream, push, proto |
stream_push_datagrams_total, stream_push_frames_total |
stream, push, proto |
En el servidor Prometheus integrado los mismos contadores están disponibles con las etiquetas name (el stream) y push; en central se les añade node. La profundidad de protocolo — stream_push_srt_* y stream_push_rist_* — llega en el scrape del nodo junto con la opción de contadores extendidos.
# velocidad de envío por destino
rate(stream_push_bytes_total{stream="tv1"}[1m])
# la salida total del stream por todos los pushes
sum by (name) (rate(stream_push_bytes_total{name="tv1"}[1m]))
# canales que se cortan: reconexiones en cinco minutos
increase(stream_push_reconnects_total[5m]) > 0
# pérdidas en el camino hacia el receptor por SRT/RIST
rate(stream_push_retransmitted_packets_total[1m])
Cómo leer el estado¶
status: errorcon unerrorno vacío — no se está enviando nada y la causa está nombrada. El push, aun así, sigue levantándose solo, una vez cada cinco segundos.status: runningperobitrate_kbiten cero — la conexión está, los datos no: compruebe si el propio stream está corriendo.status: runningcon unreconnects_totalcreciente — el canal se corta y se recupera; mireopened_at, muestra la edad de la conexión actual.status: disabled— el push está apagado en la configuración. Sus contadores quedan congelados en sus últimos valores; no se pierde nada.
El servidor Prometheus integrado¶
Sapsan contiene un servidor Prometheus integrado: la HTTP API /api/v1/query, /api/v1/query_range, /api/v1/series, /api/v1/labels, /api/v1/label/{name}/values, /api/v1/status/buildinfo sobre su propio almacén en memoria. Para Grafana es un datasource listo: añada un datasource de tipo Prometheus, apúntelo a la URL del servidor — y construya gráficos sin desplegar una TSDB. Los gráficos de la UI integrada se alimentan de esas mismas consultas.
Límites por construcción:
- la historia es de unas horas, en memoria; tras un reinicio el almacén queda vacío. Es el canal del «qué está pasando ahora», no un archivo de métricas;
- PromQL está soportado como un subconjunto: selectores con matchers de etiquetas,
rate/irate/increase, las agregacionessum/avg/min/max/countconby()/without(), aritmética,offset. Una construcción no soportada devuelve un error explícito, no un resultado distorsionado; - el conjunto de series es el básico; las series profundas (por PID, SRT, RTP) aparecen con la opción de contadores extendidos.
Consultas a un solo servidor¶
# velocidad de captura del stream, bytes/s
rate(stream_input_bytes_total{stream="cam1"}[1m])
# errores de entrada en los últimos 5 minutos
increase(stream_input_errors_total{stream="cam1"}[5m])
# ¿el archivo se está escribiendo con errores?
increase(stream_dvr_write_segments_failed_total{stream="cam1"}[10m])
# tráfico total de captura del servidor
sum(rate(stream_input_bytes_total[1m]))
# memoria usada por los streams, por parte del pipeline
sum by (part) (stream_memory_bytes)
En un servidor único las series no llevan etiqueta node.
Consultas a central¶
Central y los nodos Sapsan forman un único complejo: central recoge las estadísticas de todos los nodos, y su servidor Prometheus integrado sirve exactamente el mismo API. Hay una sola diferencia, pero que lo atraviesa todo — la etiqueta node:
- cada serie lleva
node="nombre-del-nodo"— se ve qué nodo la produjo; - un stream puede dar varias series: un stream de clúster se sirve por partes en distintos nodos;
nodeaparece en/api/v1/labelsy enlabel_values(node)— de ahí se hace una variable de dashboard de Grafana con un desplegable de nodos.
# el stream completo, sin importar en cuántos nodos vive
sum without (node) (rate(stream_input_bytes_total{stream="cam1"}[1m]))
# tráfico de captura del complejo, desglosado por nodos
sum by (node) (rate(stream_input_bytes_total[1m]))
# en qué nodos vive ahora el stream — mire la etiqueta node del resultado
stream_input_bytes_total{stream="cam1"}
El mismo dashboard funciona tanto contra un Sapsan único como contra central: agregue con sum without (node) — en un servidor único la etiqueta no existe y la agregación no cambia nada.
El scrape de Prometheus: su propia monitorización¶
La opción «contadores extendidos»
Los endpoints del scrape están disponibles solo con la opción de licencia de contadores extendidos. Sin ella, use el servidor Prometheus integrado y Retroview.
Para las instalaciones con infraestructura de monitorización propia, Sapsan sirve métricas en el formato de texto de Prometheus:
GET /streamer/api-v4/live-metrics— streams y entradas;GET /streamer/api-v4/sessions-metrics— sesiones de reproducción;GET /streamer/api-v4/dvr/metrics— discos y catálogo del archivo;GET /streamer/api-v4/runtime/metrics— el proceso y el asignador.
Diferencias respecto al servidor Prometheus integrado: la historia se guarda de su lado y solo la limita el retention de su TSDB; está disponible la profundidad completa, incluidas las métricas de protocolo por PID/SRT/RTP; el alerting es su alertmanager. Los contadores son monótonos y sobreviven a la observación de los reinicios — este es también el canal para la conciliación de facturas de su lado.
Contadores extendidos¶
La opción de contadores extendidos abre el detalle causal y de protocolo en todas partes a la vez: en las secciones input y dvr.write del Admin API, en las series del servidor Prometheus integrado y en el scrape de Prometheus. A la telemetría de Retroview estos contadores van siempre, independientemente de la opción — por eso el análisis de causas está disponible en Retroview incluso sin ella.
Causas de los errores de entrada¶
El errors agregado responde «en la entrada hay un problema»; el desglose causal responde «cuál exactamente». Las causas vienen en dos familias:
- defectos de medios de un stream vivo —
lost_packets,broken_payload,desync,ts_pat: los datos llegan, pero dañados; - fallos de la fuente —
connection_closed,connection_refused,timeout,http_request_error,not_found,denied,decode_error,protocol_error,io_error,other: la fuente murió y la causa de muerte está clasificada.
La clasificación sale de la categoría tipada del error y no del texto del log, y cubre también los fallos al abrir una fuente. Cada fallo entra además en el errors agregado; los fallos repetidos en los reintentos se cuentan cada vez — la frecuencia de eventos es exactamente la señal de que «la fuente sigue muerta». Las paradas normales — reconfiguración, fin del stream, desalojo por una fuente de mayor prioridad — no se cuentan como errores.
La serie del desglose¶
En el servidor Prometheus integrado el desglose viaja como una serie con la etiqueta de causa — errors_detail_total{name, cause}; en central las series llevan también node. Las causas sin eventos no crean series.
# errores del stream en 5 minutos, desglosados por causa
sum by (cause) (increase(errors_detail_total{name="tv1"}[5m]))
MPEG-TS por cada PID¶
Por cada PID del flujo de transporte: errors_ts_cc (violaciones del continuity counter — el indicador principal de pérdidas de transporte), errors_ts_tei, errors_ts_scrambled (no se retiró el cifrado — problemas de CAM/llaves), errors_ts_psi_checksum, PES rotos, buckets de jitter del PCR, vaciamientos del búfer del decodificador (HRD).
Por qué: este es material de monitorización de nivel broadcast. Una alerta sobre rate(errors_ts_cc) > 0 por cada PID caza la degradación del transporte antes de que la vea el espectador; el jitter del PCR y el HRD muestran si el stream sobrevivirá a un decodificador de hardware.
SRT¶
RTT y su variación, pérdidas y retransmisiones en ambas direcciones, descartes por llegar tarde, llenado de los búferes, tiempos de espera de keepalive.
Por qué: ajustar latency al canal real y diagnosticar «quién pierde — nosotros o el lado remoto». Las retransmisiones crecen con el RTT estable — pérdidas en el camino; el RTT salta — el canal está congestionado.
RTP/RTSP por canal¶
Paquetes perdidos, saltos y bloqueos de los timestamps, NACK, desbordamientos de búfer — por cada canal RTP (video/audio) de una cámara.
Por qué: diagnóstico del parque de cámaras — qué cámara está fallando, antes de que lleguen las quejas por artefactos.
Rendimiento de escritura del DVR¶
segments_written_slow, segments_written_delayed, segments_written_collapsed, segments_discontinuity.
Por qué: una señal temprana de «el disco no da abasto». La proporción de slow/delayed crece con el mismo tráfico — el almacenamiento se está degradando; discontinuity significa huecos que luego verá en recorded_hours.
Retroview¶
Retroview es la monitorización en la nube del fabricante. Una vez por minuto Sapsan envía telemetría — el catálogo completo de contadores, incluido todo el detalle extendido — y Retroview la guarda durante meses. En el servidor no hay que configurar nada: el canal funciona junto con la licencia.
Cuándo ir a Retroview y no al servidor Prometheus integrado:
- historia y tendencias — el crecimiento del tráfico y del número de streams a lo largo de los meses, la planificación de capacidad;
- post-mortems — qué le pasaba al stream de noche: la telemetría sobrevive a los reinicios y no depende de la memoria del servidor;
- conciliación de consumo — los contadores son muestreados en el tiempo por un sistema externo, así que la factura se puede verificar aunque los servidores se hayan reiniciado;
- análisis de causas sin la opción de contadores extendidos — la telemetría siempre lleva el detalle causal;
- alertas listas — reglas de producto en lugar de reglas caseras.
Recetas¶
| Pregunta | Dónde mirar | Señal del problema |
|---|---|---|
| ¿El stream está vivo? | el status en la API; rate(stream_input_bytes_total[1m]) en el Prometheus integrado |
el estado no es running; la velocidad de captura cayó a cero |
| ¿La reserva está lista? | inputs[] en la lista de estadísticas |
status es error o unchecked; checked_ago_ms muy por encima del intervalo de comprobación |
| ¿Estamos viviendo en la reserva? | source.url; num_sec_on_secondary_input |
la URL no es la primera del config; los segundos en la reserva siguen creciendo |
| ¿La entrada se está degradando? | input.errors, input.retries; las causas — contadores extendidos o Retroview |
los contadores crecen con el stream vivo |
| ¿El push llega? | pushes[].status y bitrate_kbit; rate(stream_push_bytes_total[1m]) |
el estado no es running; la velocidad en cero con el stream vivo |
| ¿Por qué no funciona el push? | pushes[].error |
la causa en palabras: conexión rechazada, tiempo de espera, rechazo de publicación |
| ¿Se corta el canal hasta el receptor? | pushes[].reconnects_total, opened_at |
las reconexiones crecen; opened_at se renueva cada pocos minutos |
| ¿El archivo se está escribiendo? | dvr.to en la lista; dvr.write.segments_failed en el GET individual |
to se retrasa respecto al tiempo real; segments_failed crece |
| ¿Hay huecos en el archivo? | dvr.recorded_hours contra la ventana to - from |
las horas grabadas son notablemente menos que la anchura de la ventana |
| ¿Los discos dan abasto? | segments_written_slow/_delayed (opción); dvr_disk_free_bytes en el scrape |
la proporción de escrituras lentas crece; el espacio libre mengua más rápido de lo esperado |
| ¿El servidor está sano? | runtime/metrics: process_rss_bytes; el Prometheus integrado: stream_memory_bytes |
la memoria crece de forma monótona sin crecimiento de carga |
| ¿Qué pasó de noche? | Retroview | — |