Ce guide couvre tous les outils disponibles pour observer l'état de Mayan EDMS en production : logs applicatifs, journal d'erreurs intégré, healthchecks Docker, surveillance des workers Celery avec Flower, et intégration Sentry.
Le système de logging de Mayan est configuré dynamiquement au démarrage via dictConfig(). La configuration est centralisée dans l'app mayan.apps.logging.
Variables d'environnement :
| Variable | Défaut | Options | Description |
|---|---|---|---|
MAYAN_LOGGING_LEVEL |
ERROR (prod), DEBUG (dev) |
DEBUG INFO WARNING ERROR CRITICAL |
Niveau de log global |
MAYAN_LOGGING_HANDLERS |
console |
console logfile |
Destination des logs |
MAYAN_LOGGING_LOG_FILE_PATH |
{MEDIA_ROOT}/error.log |
chemin absolu | Fichier de log |
MAYAN_LOGGING_ENABLE |
True |
True False |
Activer le système de logging |
MAYAN_LOGGING_DISABLE_COLOR_FORMATTER |
False |
True False |
Désactiver les couleurs ANSI |
Exemple — activer les logs fichier en production :
# docker-compose.yml
environment:
MAYAN_LOGGING_LEVEL: WARNING
MAYAN_LOGGING_HANDLERS: logfile
MAYAN_LOGGING_LOG_FILE_PATH: /var/lib/mayan/error.log
Console (mayan_intermediate) : inclut le nom de fonction, numéro de ligne, PID.
Fichier (mayan_logfile) : ajoute un timestamp ISO.
2024-01-15 14:23:05,112 ERROR [mayan.apps.ocr.tasks:42] pid:1234 task_document_version_page_ocr_process
Rotation automatique :
error.log, error.log.1 … error.log.5)Sans Docker :
# Flux en temps réel
tail -f /var/lib/mayan/error.log
# Filtrer par niveau
grep "ERROR\|CRITICAL" /var/lib/mayan/error.log
# Dernières 100 lignes
tail -100 /var/lib/mayan/error.log
Avec Docker :
# Logs du frontend (Gunicorn + Django)
docker compose logs -f app
# Logs d'un worker spécifique
docker compose logs -f worker_d
# Tous les services en même temps
docker compose logs -f --tail=50
# Filtrer par mot-clé
docker compose logs app 2>&1 | grep -i "error\|exception"
| Environnement | Niveau recommandé | Volume |
|---|---|---|
| Développement | DEBUG |
Très élevé |
| Staging | WARNING |
Modéré |
| Production stable | ERROR |
Faible |
| Débogage en prod | WARNING temporairement |
Modéré |
Mayan dispose d'un système de journal d'erreurs par objet stocké en base de données. Chaque document, tâche OCR, signature, etc. peut avoir son propre historique d'erreurs.
StoredErrorLog (par app)
└── ErrorLogPartition (par instance d'objet, via GenericFK)
└── ErrorLogPartitionEntry (entrée individuelle)
ErrorLoggingMiddleware capture toutes les exceptions non géréesVia l'interface web :
| Vue | URL |
|---|---|
| Journal global (toutes les apps) | /logging/global-error-log/ |
| Erreurs d'un document | Document → Actions → Journal d'erreurs |
| Vider le journal d'un objet | Document → Actions → Vider le journal d'erreurs |
Via l'API REST :
# Lister les entrées d'erreur d'un objet (ex. Document pk=42)
# content_type_id = ID du ContentType Django pour Document
curl -H "Authorization: Token <token>" \
"https://mayan.example.com/api/v4/errors/{content_type_id}/42/"
# Supprimer une entrée
curl -X DELETE -H "Authorization: Token <token>" \
"https://mayan.example.com/api/v4/errors/{content_type_id}/42/{entry_id}/"
Via le shell Django :
from mayan.apps.logging.models import GlobalErrorLogPartitionEntry
# Voir les 20 dernières erreurs globales
for entry in GlobalErrorLogPartitionEntry.objects.order_by('-datetime')[:20]:
print(f"[{entry.datetime}] {entry.text[:120]}")
| Source | Où chercher |
|---|---|
| OCR échoué | Document → Version → Journal d'erreurs |
| Signature invalide | Document → Signatures → Détail |
| Source (email, watchfolder) | Administration → Sources → Journal |
| Workflow bloqué | Administration → Workflows → Instances → Erreurs |
Le compose Mayan configure le driver json-file avec rotation automatique pour tous les services :
logging:
driver: "json-file"
options:
max-size: "100m" # 100 MB par fichier
max-file: "3" # 3 fichiers conservés
mode: "non-blocking" # n'bloque pas le conteneur si le buffer est plein
Localisation des fichiers de log Docker :
# Chemin hôte (Linux)
/var/lib/docker/containers/{container_id}/{container_id}-json.log
# Via Docker CLI (plus simple)
docker compose logs app # depuis le compose
docker logs mayan-app-1 # depuis le nom du conteneur
Lecture avec jq :
# Dernières 50 lignes du log JSON formatées
docker inspect mayan-app-1 --format '{{.LogPath}}' | \
xargs tail -50 | \
jq -r '.log'
Chaque service du compose Mayan a un healthcheck configuré. Voici les détails :
healthcheck:
test: ["CMD-SHELL", "python -c \"import sys,urllib.request; r=urllib.request.urlopen('http://127.0.0.1:8000/', timeout=5); sys.exit(0 if r.status<400 else 1)\""]
interval: 30s
timeout: 5s
retries: 10
start_period: 30s
Vérifie que Gunicorn répond sur le port 8000 avec un code HTTP < 400.
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mayan"]
interval: 10s
timeout: 5s
retries: 5
healthcheck:
test: ["CMD-SHELL", "redis-cli -a $REDIS_PASSWORD ping | grep PONG"]
interval: 5s
timeout: 5s
retries: 5
healthcheck:
test: ["CMD-SHELL", "rabbitmq-diagnostics ping"]
interval: 30s
timeout: 5s
retries: 5
healthcheck:
test: ["CMD-SHELL", "curl -s --cacert ... https://localhost:9200/_cluster/health | grep -q '\"status\":\"yellow\\|green\"'"]
interval: 10s
timeout: 5s
retries: 20
start_period: 60s
# État de tous les services
docker compose ps
# Détail d'un service (health status + logs du check)
docker inspect mayan-app-1 | jq '.[0].State.Health'
# En une ligne
docker ps --format "table {{.Names}}\t{{.Status}}"
Exemple de sortie :
NAMES STATUS
mayan-app-1 Up 2 hours (healthy)
mayan-postgresql-1 Up 2 hours (healthy)
mayan-worker-d-1 Up 2 hours (healthy)
mayan-redis-1 Up 2 hours (healthy)
Mayan expose une interface web native pour inspecter les workers et les files de tâches, sans outil externe.
| Vue | URL | Description |
|---|---|---|
| Types de tâches | /task_manager/task_types/ |
Liste toutes les tâches définies |
| Workers | /task_manager/workers/ |
Liste les 5 workers (A–E) |
| Files d'un worker | /task_manager/workers/{name}/queues/ |
Files écoutées par un worker |
| Tâches d'une file | /task_manager/workers/{name}/queues/{queue}/ |
Types de tâches dans une file |
| Worker | Files Celery | Priorité CPU (nice) | Concurrence |
|---|---|---|---|
worker_a |
converter, events | 0 (highest) | 4 |
worker_b |
documents, parsing, indexing, workflows | 2 | 4 |
worker_c |
periodic, mailing, trash, exports | 10 | 4 |
worker_d |
ocr, file_metadata, signatures | 15 (lowest) | 1 |
worker_e |
search, search_slow | 2 | 4 |
# Lister les workers actifs
docker compose exec worker_a celery -A mayan inspect active
# Voir les tâches réservées (en attente d'exécution)
docker compose exec worker_b celery -A mayan inspect reserved
# Statistiques d'un worker
docker compose exec worker_d celery -A mayan inspect stats
# Annuler toutes les tâches en attente d'une file
docker compose exec worker_a celery -A mayan purge -Q ocr
# Vérifier que tous les workers répondent
docker compose exec worker_a celery -A mayan status
Flower n'est pas inclus dans le compose Mayan par défaut, mais s'ajoute facilement.
services:
flower:
image: mayan/mayan-edms:latest
restart: unless-stopped
depends_on:
- rabbitmq
- redis
environment:
MAYAN_CELERY_BROKER_URL: ${MAYAN_CELERY_BROKER_URL}
MAYAN_CELERY_RESULT_BACKEND: ${MAYAN_CELERY_RESULT_BACKEND}
command: >
celery -A mayan flower
--port=5555
--broker=${MAYAN_CELERY_BROKER_URL}
--basic_auth=admin:mot_de_passe_flower
ports:
- "5555:5555"
networks:
- mayan
Ou via l'image officielle Flower :
flower:
image: mher/flower:2.0
restart: unless-stopped
depends_on:
- rabbitmq
environment:
CELERY_BROKER_URL: ${MAYAN_CELERY_BROKER_URL}
FLOWER_BASIC_AUTH: admin:mot_de_passe_flower
FLOWER_PORT: 5555
ports:
- "5555:5555"
networks:
- mayan
http://votre-serveur:5555/
Sécurité : Flower expose les détails des tâches (arguments, résultats). Toujours protéger avec authentification et limiter l'accès réseau (VPN ou reverse proxy avec auth).
| Vue | Description |
|---|---|
| Dashboard | Vue d'ensemble workers actifs / tâches traitées |
| Workers | Détail par worker : concurrence, files écoutées, tâches/s |
| Tasks | Historique des tâches avec statut, durée, arguments |
| Broker | État des files RabbitMQ (profondeur, consommateurs) |
| Monitor | Graphes temps réel des tâches |
pip install flower
source /opt/mayan/venv/bin/activate
cd /opt/mayan
celery -A mayan flower \
--port=5555 \
--basic_auth=admin:mot_de_passe \
--broker="amqp://mayan:password@localhost//"
Mayan inclut une app mayan_statistics qui calcule des métriques sur les données stockées (documents créés, tags, types, etc.).
Accès : Administration → Statistiques
Les statistiques sont calculées de manière asynchrone (tâche Celery) et affichées sous forme de graphiques dans l'interface.
# Nombre de workers Gunicorn actifs
docker compose exec app \
ps aux | grep gunicorn | grep -v grep | wc -l
# Mémoire consommée par Gunicorn
docker compose exec app \
ps aux | grep gunicorn | awk '{sum += $6} END {print sum/1024 " MB"}'
Variables de tuning Gunicorn :
| Variable | Défaut | Description |
|---|---|---|
MAYAN_GUNICORN_WORKERS |
3 |
Nombre de processus workers |
MAYAN_GUNICORN_WORKER_CLASS |
gevent |
Classe de worker (gevent = async) |
MAYAN_GUNICORN_TIMEOUT |
120 |
Timeout d'une requête (secondes) |
MAYAN_GUNICORN_MAX_REQUESTS |
1000 |
Requêtes avant redémarrage du worker |
MAYAN_GUNICORN_REQUESTS_JITTER |
100 |
Jitter sur max_requests |
Mayan n'expose pas nativement un endpoint /metrics. Pour Prometheus, utiliser des exporters :
# docker-compose.yml — ajout d'exporters
services:
postgres_exporter:
image: prometheuscommunity/postgres-exporter:latest
environment:
DATA_SOURCE_NAME: "postgresql://mayan:password@postgresql:5432/mayan?sslmode=disable"
ports:
- "9187:9187"
redis_exporter:
image: oliver006/redis_exporter:latest
environment:
REDIS_ADDR: redis://redis:6379
REDIS_PASSWORD: ${MAYAN_REDIS_PASSWORD}
ports:
- "9121:9121"
rabbitmq_exporter:
image: kbudde/rabbitmq-exporter:latest
environment:
RABBIT_URL: http://rabbitmq:15672
RABBIT_USER: mayan
RABBIT_PASSWORD: ${MAYAN_RABBITMQ_PASSWORD}
ports:
- "9419:9419"
Mayan intègre nativement le SDK Sentry pour la remontée d'erreurs en temps réel. L'intégration couvre Django, Celery et Redis.
# docker-compose.yml
environment:
MAYAN_PLATFORMS_SENTRY_DSN: https://votre-dsn@sentry.io/123456
MAYAN_PLATFORMS_SENTRY_ENVIRONMENT: production
MAYAN_PLATFORMS_SENTRY_PROFILES_SAMPLE_RATE: "0.1" # 10% des requêtes profilées
Variables disponibles :
| Variable | Description |
|---|---|
MAYAN_PLATFORMS_SENTRY_DSN |
DSN du projet Sentry (obligatoire) |
MAYAN_PLATFORMS_SENTRY_ENVIRONMENT |
production, staging, etc. |
MAYAN_PLATFORMS_SENTRY_DEBUG |
Mode debug SDK |
MAYAN_PLATFORMS_SENTRY_PROFILES_SAMPLE_RATE |
Taux de profilage (0.0 à 1.0) |
MAYAN_PLATFORMS_SENTRY_ATTACH_STACKTRACE |
Inclure la stack trace |
MAYAN_PLATFORMS_SENTRY_SEND_DEFAULT_PII |
Envoyer les données utilisateurs |
MAYAN_PLATFORMS_SENTRY_SERVER_NAME |
Identifiant du serveur |
# Déclencher une erreur test (authentification requise)
curl -H "Authorization: Token <token_admin>" \
https://mayan.example.com/platforms/sentry/debug/
□ Logs applicatifs configurés (MAYAN_LOGGING_LEVEL=WARNING, handler=logfile)
□ Rotation des logs active (vérifier /var/lib/mayan/error.log)
□ docker compose ps → tous les services "healthy"
□ Accès à l'interface task_manager (/task_manager/workers/)
□ Flower démarré et accessible (protégé par auth)
□ Journal d'erreurs global vérifié périodiquement (/logging/global-error-log/)
□ Alertes configurées sur les healthchecks Docker (Uptime Kuma, Prometheus Alertmanager)
□ Sentry DSN configuré (optionnel mais recommandé)
□ Sauvegarde des logs hors du serveur (S3, syslog distant)
# État global de la stack
docker compose ps
# Dernières erreurs Django
docker compose logs app --tail=100 | grep -i "error\|exception\|critical"
# Workers Celery qui répondent
docker compose exec worker_a celery -A mayan status
# Files de tâches en attente (RabbitMQ)
docker compose exec rabbitmq rabbitmqctl list_queues name messages consumers
# Taille de la base de données PostgreSQL
docker compose exec postgresql psql -U mayan -c \
"SELECT pg_size_pretty(pg_database_size('mayan'));"
# Espace disque du volume média
docker compose exec app du -sh /var/lib/mayan/document_file_storage/
# Vérifier l'état d'Elasticsearch
curl -u elastic:password https://es.example.com:9200/_cluster/health?pretty