.. _task-stats:
Task statistics
---------------
:py:func:`enable_stats` attaches a lightweight recorder to a :py:class:`Huey`
instance that persists task signals (enqueued, executing, complete, error,
retrying, and so on) into a pair of `peewee `_
tables. From those tables you can compute throughput, error-rates, per-task
timing and a live view of what is currently running.
The recorder depends only on peewee and writes to any peewee ``Database`` you
give it (SQLite, Postgres, MySQL). It is the engine behind the
:ref:`Flask-Peewee admin panel ` and the
:ref:`Django admin dashboard `, but it stands on its own:
use it to feed a custom dashboard, a metrics exporter or a CLI report.
Enabling
^^^^^^^^
Signals fire in the process where a task runs, so to record task **execution**
the recorder must be enabled in the **consumer**. Call :py:func:`enable_stats`
once, in a module the consumer imports:
.. code-block:: python
import peewee
from huey import SqliteHuey
from huey.contrib.stats import enable_stats
huey = SqliteHuey('/path/to/tasks.db')
# Any peewee (or flask-peewee) Database. It need not be the huey storage,
# and a networked database (Postgres/MySQL) lets a separate web process
# read the same statistics.
stats_db = peewee.SqliteDatabase('/path/to/stats.db')
stats = enable_stats(huey, stats_db)
:py:func:`enable_stats` is idempotent per huey instance and returns a
:py:class:`HueyStats` object. Enabling it in additional processes (for example
a web app that enqueues tasks) is harmless and captures the signals that occur
there. Statistics are scoped by ``huey.name``, so several huey instances may
share one database without their data mixing.
Querying
^^^^^^^^
The :py:class:`HueyStats` object returned by :py:func:`enable_stats` (also
available afterwards as ``huey._stats``) exposes read helpers:
.. code-block:: python
stats = enable_stats(huey, stats_db)
stats.window_counts() # {'complete': 1200, 'error': 3, ...} last 24h
stats.task_breakdown() # per-task executed/completed/errors/avg
stats.throughput(minutes=60) # {'complete': [...], 'error': [...]} per minute
stats.recent_events(limit=50) # most recent events, newest first
stats.inflight() # tasks currently executing
Two tables are created when the recorder starts (unless ``create_tables=False``):
``huey_event``, an append-only event log trimmed to the retention settings, and
``huey_inflight``, one row per currently-executing task. Writes are buffered and
flushed by a background thread, so recording adds negligible overhead to task
execution.
API
^^^
.. py:function:: enable_stats(huey, db[, **options])
Attach a stats recorder to ``huey`` and begin writing task events to
``db``. Idempotent per huey instance. Returns a :py:class:`HueyStats`.
:param huey: the :py:class:`Huey` instance to monitor.
:param db: a peewee ``Database`` or flask-peewee ``Database`` in which the
``huey_event`` and ``huey_inflight`` tables live.
:param int retention_hours: how long to keep events, in hours (default 48).
:param int max_events: maximum events retained per queue (default 2000).
:param bool capture_args: also store a truncated repr of each task's args
and kwargs (default False).
:param bool create_tables: create the tables if they do not exist
(default True).
.. py:class:: HueyStats
Returned by :py:func:`enable_stats`. Query helpers:
.. py:method:: window_counts(seconds=86400)
Return ``{signal: count}`` over the last ``seconds``.
.. py:method:: task_breakdown()
Return a list of per-task dicts with ``executed``, ``completed``,
``errors``, ``retries`` and average ``avg`` duration.
.. py:method:: throughput(minutes=60)
Return ``{'complete': [...], 'error': [...]}``, one bucket per minute,
oldest first.
.. py:method:: recent_events(limit=50)
Return the most recent events, newest first.
.. py:method:: inflight()
Return the tasks that are currently executing.