Instrumentation & metrics¶
InstrumentableHttpClient
extends BaseHttpClient with request metrics — latency,
status, and error counts — emitted through a pluggable sink. When no sink is
enabled it behaves exactly like BaseHttpClient with negligible overhead.
Subclass it the same way you would BaseHttpClient:
from asyncly.client.metrics.instrumentable_client import InstrumentableHttpClient
class CatfactClient(InstrumentableHttpClient):
...
Enabling a sink¶
Or scope metrics to a block with the context manager:
Each completed request calls sink.observe_request(...) with the client name,
method, resolved route, status, duration, and error type.
Sinks¶
Prometheus¶
Requires the prometheus extra.
PrometheusSink
records a histogram of request durations labeled by client, method, route, and
status:
from asyncly.client.metrics.sinks.prometheus import PrometheusSink
sink = PrometheusSink(namespace="asyncly", subsystem="client")
client.enable_metrics(sink)
OpenTelemetry¶
Requires the opentelemetry extra.
OpenTelemetrySink
records through an OpenTelemetry Meter:
from opentelemetry import metrics
from asyncly.client.metrics.sinks.opentelemetry import OpenTelemetrySink
sink = OpenTelemetrySink(meter=metrics.get_meter("asyncly"))
client.enable_metrics(sink)
Noop¶
NoopSink is the default — it does
nothing and adds no overhead.
Route labels¶
To avoid high-cardinality metrics, request paths are normalized into a route
label by default_route_resolver,
which replaces numeric and UUID path segments with :id (so /cats/42 becomes
/cats/:id). Pass your own route_resolver to enable_metrics /
instrument to customize this.
Custom sinks¶
Any object implementing the
MetricsSink protocol works: