Observability¶
haystack-py provides two levels of observability: metrics hooks for structured telemetry and standard Python logging for operational insight.
See also
Core for the full MetricsHooks API reference.
Metrics Hooks¶
MetricsHooks is a frozen dataclass with optional
callbacks for key events. Pass it to the WebSocket client or server to
receive structured telemetry.
from hs_py import MetricsHooks
hooks = MetricsHooks(
on_ws_connect=lambda addr: print(f"Connected: {addr}"),
on_ws_disconnect=lambda addr: print(f"Disconnected: {addr}"),
on_ws_message_sent=lambda op, size: print(f"Sent {op}: {size} bytes"),
on_ws_message_recv=lambda op, size: print(f"Recv {op}: {size} bytes"),
on_request=lambda op, duration: print(f"{op} took {duration:.3f}s"),
on_error=lambda op, err_type: print(f"{op} failed: {err_type}"),
)
Available Hooks¶
Hook |
Signature |
Triggered When |
|---|---|---|
|
|
WebSocket connection established |
|
|
WebSocket connection closed |
|
|
Message sent (op name, payload bytes) |
|
|
Message received (op name, payload bytes) |
|
|
Request completed (op name, seconds) |
|
|
Request failed (op name, error type string) |
All hooks are optional — provide only the ones you need. Hooks that raise exceptions are logged at DEBUG level and silently suppressed to avoid disrupting the transport.
Using with WebSocket Client¶
from hs_py.ws_client import WebSocketClient
from hs_py import MetricsHooks
hooks = MetricsHooks(
on_ws_connect=lambda addr: stats.incr("ws.connect"),
on_ws_disconnect=lambda addr: stats.incr("ws.disconnect"),
on_ws_message_sent=lambda op, size: stats.histogram("ws.send_bytes", size),
on_ws_message_recv=lambda op, size: stats.histogram("ws.recv_bytes", size),
)
async with WebSocketClient("ws://host/api/ws", metrics=hooks) as ws:
await ws.read("point")
Prometheus Example¶
from prometheus_client import Counter, Histogram
from hs_py import MetricsHooks
REQUEST_DURATION = Histogram(
"haystack_request_seconds", "Haystack op duration", ["op"]
)
REQUEST_ERRORS = Counter(
"haystack_request_errors_total", "Haystack op errors", ["op"]
)
WS_CONNECTIONS = Counter(
"haystack_ws_connections_total", "WebSocket connections"
)
hooks = MetricsHooks(
on_ws_connect=lambda addr: WS_CONNECTIONS.inc(),
on_request=lambda op, dur: REQUEST_DURATION.labels(op=op).observe(dur),
on_error=lambda op, err: REQUEST_ERRORS.labels(op=op).inc(),
)
Logging¶
haystack-py uses the standard logging module. All loggers are under the
hs_py namespace.
Quick Setup¶
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(name)s %(levelname)s %(message)s",
)
Logger Hierarchy¶
Logger |
Events |
|---|---|
|
HTTP requests, responses, auth, retry |
|
SCRAM handshake steps, token exchange |
|
Incoming requests, auth, errors, middleware |
|
WebSocket frames, heartbeat, connection lifecycle |
|
WS client requests, reconnection, pool events |
|
WS server dispatch, push, connection limits |
Targeted Logging¶
Enable verbose logging for specific modules without flooding the console:
import logging
# Only show debug output for the client
logging.getLogger("hs_py.client").setLevel(logging.DEBUG)
# Quiet the WebSocket heartbeat chatter
logging.getLogger("hs_py.ws").setLevel(logging.WARNING)
Log Levels¶
Level |
What You See |
|---|---|
|
Request/response bodies, SCRAM steps, frame contents, reconnection attempts — useful for troubleshooting but verbose in production |
|
Connection lifecycle (opened, closed), authentication success, server startup |
|
Unexpected conditions that are handled (auth retry, reconnection, connection limit reached) |
|
Unrecoverable failures (connection lost, auth rejected, handler exceptions) |