Django instrumentation for Watchlog APM with JSON OTLP export
Project description
django_watchlog_apm
🔗 Website: https://watchlog.io
django_watchlog_apm is a lightweight APM (Application Performance Monitoring) integration for Django applications, built on OpenTelemetry. It automatically instruments your Django application to send traces to the Watchlog Agent.
Features
- ✅ Auto-instrumentation for Django views, middleware, and HTTP requests
- ✅ Database query tracing (PostgreSQL via psycopg2)
- ✅ JSON-over-HTTP exporter (OTLP) compatible with Watchlog Agent
- ✅ Environment detection (local vs Kubernetes)
- ✅ Configurable sampling with error and slow span filtering
- ✅ Non-blocking - errors never crash your application
- ✅ Zero configuration - works out of the box
Installation
From PyPI
pip install django-watchlog-apm
Optional: Database Query Tracing
To enable PostgreSQL database query tracing, install the additional package:
pip install opentelemetry-instrumentation-psycopg2==0.56b0
Note: Database query tracing is optional. The package will work without it, but database queries won't be traced.
Quick Start
Step 1: Initialize APM in wsgi.py or asgi.py
Important: You must call instrument_django() before Django loads its settings.
For WSGI (wsgi.py):
import os
from django.core.wsgi import get_wsgi_application
# 1) Import and call instrumentation BEFORE Django setup
from django_watchlog_apm.instrument import instrument_django
instrument_django(
service_name="my-django-app", # Your service name
otlp_endpoint="http://watchlog-agent:3774/apm", # Watchlog agent endpoint
)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
application = get_wsgi_application()
For ASGI (asgi.py):
import os
from django.core.asgi import get_asgi_application
# 1) Import and call instrumentation BEFORE Django setup
from django_watchlog_apm.instrument import instrument_django
instrument_django(
service_name="my-django-app",
otlp_endpoint="http://watchlog-agent:3774/apm",
)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
application = get_asgi_application()
Step 2: Run Watchlog Agent
Make sure the Watchlog Agent is running and accessible. The agent should be listening on port 3774 with the /apm endpoint.
Step 3: Test Your Application
Make requests to your Django application. Traces will be automatically sent to the Watchlog Agent.
Configuration Options
instrument_django() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
service_name |
str |
required | Name of your Django service (appears in Watchlog dashboard) |
otlp_endpoint |
str |
http://localhost:3774/apm |
Base OTLP URL. Final URL will be {endpoint}/{service_name}/v1/traces |
headers |
dict |
{} |
Additional HTTP headers for OTLP requests |
batch_max_size |
int |
200 |
Maximum spans per batch export |
batch_delay_ms |
int |
5000 |
Delay (milliseconds) between batch exports |
sample_rate |
float |
1.0 |
Random sampling rate (0.0–1.0). Note: Internally capped at 0.3 for performance |
send_error_spans |
bool |
False |
If True, always export spans with error status |
error_tps |
int |
None |
Max error spans to export per second (None = unlimited) |
slow_threshold_ms |
int |
0 |
If >0, always export spans slower than this threshold (milliseconds) |
export_timeout |
float |
10.0 |
HTTP request timeout (seconds) for exporter POSTs |
Example: Advanced Configuration
instrument_django(
service_name="my-django-app",
otlp_endpoint="http://watchlog-agent:3774/apm",
sample_rate=1.0, # Try to sample all requests (capped at 0.3 internally)
send_error_spans=True, # Always export error spans
error_tps=10, # Max 10 error spans per second
slow_threshold_ms=200, # Always export spans >200ms
batch_delay_ms=1000, # Export batches every 1 second
batch_max_size=50, # Smaller batches for faster export
export_timeout=5.0, # 5 second timeout
)
Docker Setup
Connecting to Watchlog Agent in Docker
If your Django app and Watchlog Agent are in separate Docker containers, use the container name as the host:
instrument_django(
service_name="my-django-app",
otlp_endpoint="http://watchlog-agent:3774/apm", # Use container name
)
Make sure both containers are on the same Docker network:
# Create network
docker network create my-network
# Run Watchlog Agent
docker run -d --name watchlog-agent --network my-network -p 3774:3774 watchlog/agent:latest
# Run Django app
docker run -d --name django-app --network my-network -p 8000:8000 my-django-app
What Gets Traced?
Automatically Traced:
- ✅ HTTP Requests: All incoming requests to Django views
- ✅ Database Queries: PostgreSQL queries (if
opentelemetry-instrumentation-psycopg2is installed) - ✅ External HTTP Calls: Outgoing requests made with the
requestslibrary - ✅ View Execution: Django view function execution
- ✅ Middleware: Django middleware processing
Trace Structure:
Each trace contains:
- Root Span: The HTTP request (e.g.,
GET /api/users/) - Child Spans: Database queries, external HTTP calls, etc.
Example trace hierarchy:
GET /api/users/
├── SELECT * FROM users WHERE id = 1
├── GET https://external-api.com/data
└── POST /api/users/1/update
Manual Custom Spans
You can create custom spans using the OpenTelemetry API:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
def my_view(request):
with tracer.start_as_current_span("custom.operation") as span:
span.set_attribute("user.id", request.user.id)
span.set_attribute("operation.type", "data_processing")
# Your business logic here
result = process_data()
span.set_attribute("result.count", len(result))
return JsonResponse({"result": result})
Environment Detection
The package automatically detects the environment:
- Local (non-Kubernetes): Uses the provided
otlp_endpoint(default:http://localhost:3774/apm) - Kubernetes (in-cluster): Automatically switches to
http://watchlog-python-agent.monitoring.svc.cluster.local:3774/apm
Detection checks (in order):
- Existence of
/var/run/secrets/kubernetes.io/serviceaccount/token - Presence of
kubepodsin/proc/1/cgroup - DNS lookup of
kubernetes.default.svc.cluster.local
Error Handling
Important: This package is designed to never crash your application. All errors are silently handled:
- ✅ Export failures are swallowed (traces are lost, but app continues)
- ✅ Instrumentation failures are logged but don't prevent Django from starting
- ✅ Missing optional dependencies (like psycopg2 instrumentation) are gracefully handled
If you want to see error logs, configure logging:
import logging
logging.getLogger("django_watchlog_apm").setLevel(logging.INFO)
Troubleshooting
Traces Not Appearing in Watchlog
-
Check Agent Connection: Verify the Watchlog Agent is running and accessible
curl http://watchlog-agent:3774/ -
Check Network: Ensure Django container can reach the agent
docker exec django-app ping watchlog-agent
-
Check Logs: Look for export errors in Django logs
docker logs django-app | grep "django_watchlog_apm"
-
Verify Endpoint: The final URL should be
http://watchlog-agent:3774/apm/{service_name}/v1/traces
Database Queries Not Traced
- Ensure
opentelemetry-instrumentation-psycopg2==0.56b0is installed - Check that you're using
psycopg2(notpsycopg2-binaryfor instrumentation, thoughpsycopg2-binaryworks) - Verify the package is imported before Django starts
High Memory Usage
- Reduce
batch_max_size(default: 200) - Increase
batch_delay_msto export less frequently - Lower
sample_rate(though it's capped at 0.3 internally)
Sample Rate Limitation
Important: The sample_rate parameter is internally capped at 0.3 (30%) for performance reasons. Even if you set sample_rate=1.0, only approximately 30% of spans will be exported.
To export more spans:
- Set
slow_threshold_ms=0to export all slow spans - Set
send_error_spans=Trueto always export error spans - Use both in combination for maximum coverage
License
MIT © Mohammadreza
Built for Watchlog.io
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file django_watchlog_apm-1.1.0.tar.gz.
File metadata
- Download URL: django_watchlog_apm-1.1.0.tar.gz
- Upload date:
- Size: 8.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f45f6dbd07609d5a85698ff2ee6846662a6eb2f6253300ba809f67092222ce66
|
|
| MD5 |
98804f9df595b0281ad0a41837c0ae17
|
|
| BLAKE2b-256 |
d8425e2931de405bfadfc079785dc8374ef23a0c72375dcad7a461af7cd1e93e
|