Files
Aaron Boxer ced4bdb737 feat: add Prometheus metrics instrumentation
- New whisper_live/metrics.py with Counter, Gauge, Histogram metrics
- Track connections (opened/closed/rejected), transcription latency,
  audio processed, segments emitted, REST requests, and errors
- All metric helpers are no-ops when prometheus_client not installed
- --metrics_port CLI flag to expose /metrics endpoint (0 = disabled)
- Metrics integrated into server.py, base.py at key instrumentation points
- 17 new tests in tests/test_metrics.py (178 total passing)
2026-05-13 10:45:40 -04:00

123 lines
3.1 KiB
Python

"""
Prometheus metrics for WhisperLive server.
Exposes a /metrics HTTP endpoint on a configurable port for Prometheus scraping.
All metrics are optional — the server works fine without prometheus_client installed.
"""
import logging
import threading
try:
from prometheus_client import (
Counter,
Gauge,
Histogram,
start_http_server,
)
CONNECTIONS_TOTAL = Counter(
"whisperlive_connections_total",
"Total WebSocket connections accepted",
)
CONNECTIONS_ACTIVE = Gauge(
"whisperlive_connections_active",
"Currently active WebSocket connections",
)
CONNECTIONS_REJECTED = Counter(
"whisperlive_connections_rejected_total",
"Connections rejected (server full or auth failure)",
["reason"],
)
TRANSCRIPTION_LATENCY = Histogram(
"whisperlive_transcription_latency_seconds",
"Time to transcribe a single audio chunk",
buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
)
AUDIO_PROCESSED = Counter(
"whisperlive_audio_processed_seconds_total",
"Total seconds of audio processed",
)
SEGMENTS_EMITTED = Counter(
"whisperlive_segments_emitted_total",
"Total transcription segments sent to clients",
["completed"],
)
REST_REQUESTS = Counter(
"whisperlive_rest_requests_total",
"Total REST API requests",
["endpoint", "status"],
)
ERRORS = Counter(
"whisperlive_errors_total",
"Total errors by type",
["type"],
)
_AVAILABLE = True
except ImportError:
_AVAILABLE = False
def is_available():
"""Check if prometheus_client is installed."""
return _AVAILABLE
def start_metrics_server(port=9091):
"""Start the Prometheus metrics HTTP server on the given port.
Args:
port (int): Port to serve /metrics on. Default 9091.
"""
if not _AVAILABLE:
logging.warning("prometheus_client not installed; metrics endpoint disabled")
return
try:
start_http_server(port)
logging.info(f"Prometheus metrics available at http://0.0.0.0:{port}/metrics")
except Exception as e:
logging.error(f"Failed to start metrics server: {e}")
def track_connection_opened():
if _AVAILABLE:
CONNECTIONS_TOTAL.inc()
CONNECTIONS_ACTIVE.inc()
def track_connection_closed():
if _AVAILABLE:
CONNECTIONS_ACTIVE.dec()
def track_connection_rejected(reason="full"):
if _AVAILABLE:
CONNECTIONS_REJECTED.labels(reason=reason).inc()
def track_transcription_latency(seconds):
if _AVAILABLE:
TRANSCRIPTION_LATENCY.observe(seconds)
def track_audio_processed(seconds):
if _AVAILABLE:
AUDIO_PROCESSED.inc(seconds)
def track_segment_emitted(completed=True):
if _AVAILABLE:
SEGMENTS_EMITTED.labels(completed=str(completed).lower()).inc()
def track_rest_request(endpoint="/v1/audio/transcriptions", status="200"):
if _AVAILABLE:
REST_REQUESTS.labels(endpoint=endpoint, status=str(status)).inc()
def track_error(error_type="transcription"):
if _AVAILABLE:
ERRORS.labels(type=error_type).inc()