pystatsd

A lightweight, dependency-free Python client for sending counters, gauges, timers, and sets to a statsd daemon.

SDK
PyPI
v4.0.1
553stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
44/100Fair
Development Activity4
Maintenance20
Community72
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture78
Code Quality72
Innovation75
Learning Curve80

pystatsd is the reference Python client for statsd, the network daemon that aggregates application metrics and forwards them to Graphite or compatible backends. It ships three interchangeable client classes covering UDP, TCP, and Unix domain socket transports, all built on a shared base class that formats and dispatches stat updates.

Beyond basic counters and gauges, it provides a pipelining API for batching multiple stats into a single network packet, a Timer helper that works as a decorator, context manager, or manual start/stop object (with native async support), and per-call sample rates for cheaply instrumenting high-volume code paths. Ready-made Django and environment-variable configuration modules remove setup boilerplate for the most common deployment contexts.

What You Get

  • Three interchangeable client classes (StatsClient, TCPStatsClient, UnixSocketStatsClient) sharing one API for counters, gauges, timers, and sets
  • A pipeline/context-manager API that batches stats into fewer network packets
  • A Timer helper usable as a decorator, context manager, or manual start/stop for measuring code duration, with native async function support
  • Ready-made Django and environment-variable configuration modules under statsd.defaults
  • Per-call sample rate support for cheaply instrumenting high-frequency code paths

Common Use Cases

  • Recording request latency and endpoint timing for Graphite dashboards
  • Counting feature usage and business events with simple incr()/decr() calls
  • Wiring a shared statsd client into a Django settings-based application
  • Sampling noisy, high-volume events to control metric traffic to the statsd daemon

Under The Hood

Architecture pystatsd is layered around a transport-agnostic StatsClientBase (statsd/client/base.py) that implements stat formatting and dispatch (_prepare, _send_stat, _after) shared by every client; StatsClient (statsd/client/udp.py) sends over UDP by default, while TCPStatsClient and UnixSocketStatsClient (statsd/client/stream.py) share a StreamClientBase that adds connect/reconnect semantics for stream sockets. Each transport pairs with its own PipelineBase subclass (Pipeline in udp.py, StreamPipeline in stream.py) so pipeline() batches multiple stat calls into a single packet without touching the base formatting logic. Timer (statsd/client/timer.py) is composed into the base class via the .timer() method rather than inherited, letting it double as a decorator, context manager, or manually started/stopped object. Because every transport and pipeline variant inherits _prepare/_send_stat from StatsClientBase, changing that core formatting logic would ripple through all three client types at once.

Tech Stack The library has zero runtime dependencies — it is built entirely on the Python standard library (socket, functools, inspect, time, collections.deque) — and targets Python 3.10 through 3.14 plus PyPy per its GitHub Actions matrix. Packaging uses a modern pyproject.toml with a setuptools backend (setuptools>=61.2), and documentation is built with Sphinx from the docs/ directory. Optional statsd.defaults.django and statsd.defaults.env modules provide thin, dependency-free integration shims for the two most common deployment contexts.

Code Quality Testing is thorough: a single 1000+ line statsd/tests.py exercises every client, pipeline, sample-rate, and Timer code path using unittest and unittest.mock, and CI runs this matrix across six Python/PyPy versions plus a dedicated flake8 lint job and a scheduled CodeQL scan. Error handling is explicit rather than typed — there are no type hints or a py.typed marker — and the UDP client’s _send() intentionally swallows OSError/RuntimeError on send since a dropped metric shouldn’t crash the caller. Naming is consistent, idiomatic snake_case throughout, with docstrings on public methods.

API Design The public surface is deliberately small: instantiate one of three client classes with a host/port or socket path, then call incr/decr/gauge/set/timing directly, with no setup ceremony beyond that. The Timer class is the standout ergonomic touch, working interchangeably as a decorator, a context manager, or manual start()/stop() calls, including native async function support. Sample rates and prefixes are plain constructor/call arguments rather than separate configuration objects, and the defaults.django/defaults.env modules remove all remaining boilerplate for the two most common deployment contexts.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search