Django Salmon
A standardized way to observe Django's internals.
Observability is critical feature of software development, but tends to be neglected. It seems like the majority of Python APMs and observability tools have to monkey-patch Django's internals to properly observe the application. This library seeks to do that monkey-patching once, then provide a standardized set of signals/hooks to observe an application.
Usage
- Install it as a dependency:
pip install django-salmon
- Add it to your
INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"django_salmon",
# ...
]
- Connect to the relevant signals:
from django.dispatch import receiver
from django_salmon.signals import observe_cache_operation
@receiver(observe_cache_operation)
def observe_cache_ops(sender, args, result, timing, stacktrace):
print(f"{sender=}\t{timing=}=")
# stacktrace is a callable that will return the formatted stacktrace.
print("".join(stacktrace()))
Configuration
Django Salmon supports customizing the decorators that are applied to Django functionality. For example, by default the following decorators are used on all cache operations:
OBSERVING = {
"cache": [
"django_salmon.decorators.prevent_nested_observe",
"django_salmon.decorators.with_args",
"django_salmon.decorators.with_result",
"django_salmon.decorators.with_stacktrace",
"django_salmon.decorators.with_timing",
],
}
Each of these decorators will measure a different facet and include it in the context for the django_salmon.decorators.observe decorator. If you only need to measure the timing facet, use the following configuration:
OBSERVING = {
"cache": [
"django_salmon.decorators.prevent_nested_observe",
"django_salmon.decorators.with_timing",
],
}
Observing your own functions
Django Salmon supports you applying your own decorators explicitly or by using a registry and utilizing the OBSERVING configuration.
from django.dispatch import Signal
from django_salmon.decorators import observe, with_args
my_signal = Signal()
@observe(my_signal)
@with_args
def explicit_usage_example(arg1, arg2, kwarg1=None):
return "result"
You can register your own stacked decorators to the registry as well:
# settings.py
OBSERVING = {
"only_timing": [
"django_salmon.decorators.prevent_nested_observe",
"django_salmon.decorators.with_timing",
],
}
# signals.py
from django.dispatch import Signal
my_signal = Signal()
# observe.py / Or anything else you want to use
from django_salmon import registry
from django_salmon.decorators import observe
from .signals import my_signal
def observe_only_timing(func):
"""
Custom decorator factory that emits ``my_signal`` signals.
"""
return observe(my_signal)(registry.apply_decorators("only_timing", func))
# your_logic.py
from .observe import observe_only_timing
@observe_only_timing
def registry_usage_example(arg1, arg2, kwarg1=None):
return "result"
Creating your own observer decorator
You can define your own decorator to be used with the registry by adding the relevant data to the context.
An example is:
import functools
from django.dispatch import receiver, Signal
from django_salmon.decorators import observe_context, observe
def with_answer_to_everything(func):
"""
Decorator that adds the answer to life to the context.
Must be used below @observe decorator.
"""
@functools.wraps(func)
def wrapper(*func_args, **func_kwargs):
func_result = func(*func_args, **func_kwargs)
# The context will be None if OBSERVING["enabled"] is False
if (context := observe_context.get()) is not None:
# Observe some aspect of the project
context["answer_to_everything"] = 42
return func_result
return wrapper
my_signal = Signal()
@receiver(my_signal)
def my_signal_receiver(sender, answer_to_everything, **kwargs):
"""
When my_signal is raised, it will now include answer_to_everything
as a keyword argument.
"""
print(f"{answer_to_everything=}")
@observe(my_signal)
@with_answer_to_everything
def custom_decorator_example():
return "result"
Project goals
This project contains several hacks to support observing a Django application. The Django project should change to avoid the necessity of these hacks.
Going further, ideally the Django framework would consider adopting an observability API rendering this project obsolete.
Contributing
See CONTRIBUTING.md for how to contribute!
Metadata
Release files for django-salmon 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| django_salmon-0.1.0.tar.gz | 50.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_salmon-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.1 kB
Release files / django_salmon-0.1.0.tar.gz
| Download URL | django_salmon-0.1.0.tar.gz |
|---|---|
| Size | 50.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7f9e01f9ccd9f78d79b66c1af6dc9356915d48b1aefca1ddf0450f1888193af7
|
|
BLAKE2b-256 checksum How to use checksums |
b7512f96559230c75a2d4229867722c45da8ca7088df03300245a69bbc727ef4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 31, 2026.
Transparency logRelease files / django_salmon-0.1.0-py3-none-any.whl
| Download URL | django_salmon-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d11553fcf3dd86b34b7885a3756a263b892083ff735cceaa55768da7e4531472
|
|
BLAKE2b-256 checksum How to use checksums |
c15f0119ee09a19ba2200e29eb95a2cc776d9a72b0eb18c30678fe07f59b1b4b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 31, 2026.
Transparency logSecurity for django-salmon 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for django_salmon-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 31, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.