Monitoring Guide
Key Metrics
API Performance
| Metric | Target | Warning | Critical |
|---|---|---|---|
| Response time (p95) | < 200ms | > 500ms | > 2s |
| Error rate (5xx) | < 0.1% | > 1% | > 5% |
| Request throughput | Baseline | -50% | -80% |
| Database query time | < 50ms | > 200ms | > 1s |
Business Metrics
| Metric | What to Watch |
|---|---|
| QR scan volume | Daily scan count, unusual spikes or drops |
| Widget API calls | Requests per API key, rate limit hits |
| Stock movements | Transaction volume, failed transactions |
| Active companies | New registrations, churn signals |
Infrastructure
| Metric | Target |
|---|---|
| PostgreSQL connections | < 80% of max_connections |
| Disk usage (DB volume) | < 80% capacity |
MonitoringRule System
The application has a built-in monitoring system (inventory/monitors.py) that evaluates rules against product data.
Rule Types
THRESHOLD — Triggers when stock quantity crosses min/max boundaries:
{
"trigger_type": "THRESHOLD",
"condition_config": { "min": 10, "max": 1000 },
"severity": "WARNING"
}
DATE_OFFSET — Triggers when batch expiry dates approach:
{
"trigger_type": "DATE_OFFSET",
"condition_config": { "date_field": "expiration_date", "days_offset": 30 },
"severity": "CRITICAL"
}
CUSTOM — Reserved for custom expression evaluation (not yet implemented).
How Rules Are Evaluated
RuleEvaluator.evaluate_product(product_model)is called per product- Individual thresholds from product
attributes(e.g.,min_threshold,min_stock) create virtual rules - Strategy-level rules from
MonitoringRuleare evaluated via the matching monitor class - Triggered conditions create
EventLogentries with statusOPEN
EventLog States
| Status | Meaning |
|---|---|
OPEN |
Alert triggered, needs attention |
RESOLVED |
Issue addressed |
IGNORED |
Manually dismissed |
Setting Up External Alerting
Connect EventLog to external systems by adding a post-save signal:
# Example: Send alerts to external webhook
from django.db.models.signals import post_save
from inventory.models import EventLog
def notify_on_event(sender, instance, created, **kwargs):
if created and instance.rule and instance.rule.severity == 'CRITICAL':
# Send to Slack, PagerDuty, email, etc.
pass
post_save.connect(notify_on_event, sender=EventLog)
Django Logging
Backend logs are written to stdout (suitable for docker compose logs). Key loggers:
# Tail backend logs
docker compose logs -f backend
# Filter for errors
docker compose logs backend 2>&1 | grep -i error
Health Checks
The Docker Compose stack includes health checks:
- Backend: HTTP check against
/admin/login/(10s interval, 5 retries) - Database:
pg_isready(5s interval, 5 retries)
Add application-level health endpoint for deeper checks:
# Quick liveness check
curl -f http://localhost:8001/admin/login/ && echo "OK"
# Database connectivity (via Django)
docker compose exec backend python manage.py dbshell -c "SELECT 1"
Active Notifications (email + webhook)
Monitoring rules that open an EventLog are delivered through the company's
NotificationChannels (Settings → Notifications in the dashboard). Delivery is
synchronous inside the monitor run (python manage.py check_rules); failed
deliveries are retried with exponential backoff (max 5 attempts) at the start
of every subsequent run — no extra scheduler needed.
Email environment variables
| Variable | Default | Notes |
|---|---|---|
EMAIL_BACKEND |
console backend in DEBUG, SMTP otherwise |
Override to force a backend |
EMAIL_HOST |
localhost |
SMTP server |
EMAIL_PORT |
587 |
|
EMAIL_HOST_USER |
empty | |
EMAIL_HOST_PASSWORD |
empty | |
EMAIL_USE_TLS |
True |
|
DEFAULT_FROM_EMAIL |
notifications@portable-inventory.local |
Sender address |
Webhook signature
Webhook POSTs carry an X-PI-Signature: sha256=<hex> header — the HMAC-SHA256
of the raw JSON body keyed with the channel's server-generated secret. Verify
on the receiver before trusting the payload. The secret is visible only to the
owning company via the API; it is never written to delivery logs.