Metadata-Version: 2.5
Name: django-tasks-monitor
Version: 0.1.0
Summary: A monitoring dashboard for Django's built-in tasks framework. Works with any task backend.
Project-URL: Homepage, https://github.com/ajinkyapisal/django-tasks-monitor
Project-URL: Issues, https://github.com/ajinkyapisal/django-tasks-monitor/issues
License-Expression: MIT
License-File: LICENSE
Classifier: Framework :: Django
Classifier: Framework :: Django :: 6.0
Classifier: Framework :: Django :: 6.1
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.12
Requires-Dist: django>=6.0
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-django; extra == 'dev'
Description-Content-Type: text/markdown

# django-tasks-monitor

A monitoring dashboard for Django's built-in [Tasks framework](https://docs.djangoproject.com/en/stable/topics/tasks/).

Django 6 added `django.tasks`, but no way to see what your tasks are doing:
no admin pages, no failure list, no queue backlog. This package records every
task and adds a dashboard and task list to the Django admin, with retry for
failed tasks. It works with **any** task backend (the database backend from
`django-tasks-db`, RQ, Celery and others), because it listens to the signals
that `django.tasks` sends rather than reading a particular backend's storage.

![Tasks dashboard in the Django admin](docs/dashboard.png)

> Status: early. The API may change.

## Install

Requires Python 3.12+ and Django 6.0+.

```bash
pip install django-tasks-monitor
```

```python
INSTALLED_APPS = [
    # ...
    "tasks_monitor",
]
```

```bash
python manage.py migrate
```

Every task enqueued from then on is recorded. Open **Tasks monitor → Task
records** in the admin, and click **Dashboard**.

## What you get

- **Dashboard:** tasks, successes and failures (with failure rate) for the last
  hour, 24 hours or 7 days; tasks running now, waiting and scheduled; stuck
  tasks; per-task counts with average and maximum duration; the backlog per
  queue; recent failures.
- **Task list:** every task with its status, queue, timings, attempts and
  error, filterable by status, queue, backend and task, and searchable.
- **Failure details:** the exception and full traceback.
- **Retry:** select failed tasks and retry them with the same arguments, queue,
  priority and backend. The new task links back to the one it retried. Needs
  the `tasks_monitor.retry_taskrecord` permission.
- **Stuck tasks:** tasks that have been running longer than `STUCK_AFTER`,
  usually because their worker died.
- **Alerts:** a `task_failed` signal, sent after a failure is recorded.

```python
from django.dispatch import receiver
from tasks_monitor.signals import task_failed


@receiver(task_failed)
def alert(sender, record, **kwargs):
    notify_slack(f"{record.task_name} failed: {record.exception_class}")
```

## Settings

```python
TASKS_MONITOR = {
    # Task paths, or path prefixes, that are not recorded.
    "IGNORE_TASKS": [],
    # Store task arguments. Turn this off if arguments can contain personal or
    # other sensitive data. Retrying from the admin then isn't possible.
    "RECORD_ARGS": True,
    # Finished records older than this are deleted by the prune command.
    "RETENTION_DAYS": 30,
    # Seconds after which a running task counts as stuck.
    "STUCK_AFTER": 3600,
}
```

Delete old records from cron:

```bash
python manage.py tasks_monitor prune
```

## How it works

- `django.tasks` sends `task_enqueued`, `task_started` and `task_finished`
  signals. The monitor keeps one `TaskRecord` row per task result up to date
  from them. Enqueue signals fire in your web process; start and finish
  signals fire in the worker, so the worker must have `tasks_monitor`
  installed too (it does if it runs your Django project).
- Each write runs in its own savepoint, and any error is logged instead of
  raised. Recording can never break a task.
- With brokers like Redis, a worker can report a task started before the web
  process has recorded the enqueue. The monitor never moves a task back to an
  earlier status when signals arrive out of order.
- The record is only as good as the signals the backend sends. If a worker is
  killed, no finish signal arrives, and the task shows as stuck.

## Development

```bash
python3.13 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
```

`example/` is a demo project on Postgres with the `django-tasks-db` backend.
`example/e2e.py` runs real `db_worker` processes and checks recording,
failures, backlog, scheduled tasks, stuck detection after a killed worker,
retry and the admin pages:

```bash
docker run -d --name dtm-pg -e POSTGRES_HOST_AUTH_METHOD=trust \
    -e POSTGRES_DB=dtm_demo -p 55433:5432 postgres:17-alpine
.venv/bin/pip install django-tasks-db "psycopg[binary]"
.venv/bin/python example/manage.py migrate
.venv/bin/python example/e2e.py
```

To click around the admin, run `example/demo_data.py` (it creates a local-only
`admin`/`admin` user), then `example/manage.py runserver` and open
http://127.0.0.1:8000/admin/.

## Related

[django-maintenance-tasks](https://github.com/ajinkyapisal/django-maintenance-tasks):
pausable, resumable data backfills built on `django.tasks`.
