Comment voir les détails des erreurs Django avec Gunicorn?

Je viens de déployer mon projet Django (1.6) avec gunicorn et Nginx.

Il semble fonctionner correctement, mais j’ai une page où j’obtiens une erreur HTTP 500 et je ne trouve aucun détail sur l’erreur où que ce soit.

Comment puis-je obtenir gunicorn pour me montrer des erreurs?

Voici tout ce que je vois actuellement dans le fichier journal lorsque je frappe la page en me donnant l’erreur:

>tail gunicorn.errors 2014-02-21 14:41:02 [22676] [INFO] Listening at: unix:/opt/djangoprojects/reports/bin/gunicorn.sock (22676) 2014-02-21 14:41:02 [22676] [INFO] Using worker: sync 2014-02-21 14:41:02 [22689] [INFO] Booting worker with pid: 22689 ... 2014-02-21 19:41:10 [22691] [DEBUG] GET /reports/2/ 

Voici mon script bash que j’utilise pour lancer gunicorn:

 >cat gunicorn_start #!/bin/bash NAME="reports" # Name of the application DJANGODIR=/opt/djangoprojects/reports # Django project directory SOCKFILE=/opt/djangoprojects/reports/bin/gunicorn.sock # we will communicte using this unix socket USER=reportsuser # the user to run as GROUP=webapps # the group to run as NUM_WORKERS=4 # how many worker processes should Gunicorn spawn DJANGO_SETTINGS_MODULE=reports.settings # which settings file should Django use DJANGO_WSGI_MODULE=reports.wsgi # WSGI module name #echo "Starting $NAME as `whoami`" # Activate the virtual environment cd $DJANGODIR source pythonenv/bin/activate export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE export PYTHONPATH=$DJANGODIR:$PYTHONPATH # Create the run directory if it doesn't exist RUNDIR=$(dirname $SOCKFILE) test -d $RUNDIR || mkdir -p $RUNDIR # Start your Django Unicorn # Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon) exec gunicorn ${DJANGO_WSGI_MODULE}:application \ --name $NAME \ --workers $NUM_WORKERS \ --user=$USER --group=$GROUP \ --log-level=debug \ --bind=unix:$SOCKFILE \ --error-logfile /opt/djangoprojects/reports/bin/gunicorn.errors \ --log-file /opt/djangoprojects/reports/bin/gunicorn.errors 

Plus d’informations:

Je commence / arrête gunicorn avec ce script init.d que j’ai copié et modifié en utilisant sudo service reports start|stop|restart :

 >cat /etc/init.d/reports #!/bin/sh ### BEGIN INIT INFO # Provides: django_gunicorn # Required-Start: $local_fs $network $remote_fs # Required-Stop: $local_fs $network $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Starts django_unicorn reports at boot time. # Description: Starts django_unicorn reports at boot time. ### END INIT INFO name=`basename $0` dir="/opt/djangoprojects/reports" cmd="${dir}/bin/gunicorn_start" pid_file="/var/run/$name.pid" log_file="${dir}/bin/reports.log" get_pid() { cat "$pid_file" } is_running() { [ -f "$pid_file" ] && ps `get_pid` > /dev/null 2>&1 } case "$1" in start) if is_running; then echo "Already running" else echo -n "Starting ${name}... " cd "$dir" #sudo -u "$user" $cmd &>> "$log_file" $cmd &>> "$log_file" & echo $! > "$pid_file" if ! is_running; then echo "Unable to start; see $log_file" exit 1 else echo "[STARTED]" fi fi ;; stop) if is_running; then echo -n "Stopping ${name}... " kill `get_pid` for i in {1..10} do if ! is_running; then break fi echo -n "." sleep 1 done echo if is_running; then echo "Not stopped; may still be shutting down or shutdown may have failed" exit 1 else echo "[STOPPED]" if [ -f "$pid_file" ]; then rm "$pid_file" fi fi else echo "Not running" fi ;; restart) $0 stop if is_running; then echo "Unable to stop, will not attempt to start" exit 1 fi $0 start ;; status) if is_running; then echo "[RUNNING]" else echo "[STOPPED]" exit 1 fi ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac exit 0 

D’après votre commentaire, je pense qu’il s’agit d’un problème de configuration sur votre site django, pas un problème de journal gunicorn, les journaux ne montreront pas plus que django.

Voici un exemple de la façon dont vous pouvez configurer django pour envoyer un journal à votre fichier (au lieu de l’envoyer aux administrateurs par e-mail par défaut):

 LOGGING = { 'version': 1, 'disable_existing_loggers': True, 'formatters': { 'verbose': { 'format': '%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(module)s %(process)d %(thread)d %(message)s' } }, 'handlers': { 'gunicorn': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'verbose', 'filename': '/opt/djangoprojects/reports/bin/gunicorn.errors', 'maxBytes': 1024 * 1024 * 100, # 100 mb } }, 'loggers': { 'gunicorn.errors': { 'level': 'DEBUG', 'handlers': ['gunicorn'], 'propagate': True, }, } } 

Lisez la configuration de la journalisation (elle fournit des explications très détaillées sur les options des parameters du journal) et étudiez le fichier django / utils / log.py pour configurer django loggin afin qu’il apparaisse plus détaillé sur les journaux gunicorn.

Vérifiez également cette réponse et celle- ci fournit des exemples de parameters pour envoyer des erreurs de journaux directement dans un fichier. Et pensez à utiliser Sentry pour gérer les erreurs de journal, comme le recommandent les gars de Django.

J’espère que cela t’aides.

1. envoyer des erreurs à la console

Ce sont les loggers qui utilisent mail_admins par défaut (voir django/utils/log.py ):

  'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, 'django.security': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, 

vous devez changer les gestionnaires pour accéder à la console afin qu’elle apparaisse dans votre journal gunicorn plutôt que d’envoyer des e-mails avec mail_admins . Veuillez noter que ce n’est pas aussi bavard que lorsque DEBUG=True .

  'loggers': { 'django': { 'level': 'ERROR', 'handlers': ['console'], }, } 

2. envoi d’erreurs via mail_admins

Également en fonction de la configuration de la journalisation, créez explicitement un gestionnaire qui appelle mail_admins ; par exemple basé sur django/utils/log.py :

  'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler' }, }, 'loggers': { 'django': { 'handlers': ['mail_admins'], }, } 

Cela nécessite que vous définissiez les settings liés au courrier électronique.

3. autres solutions

Si vous ne cherchiez pas la solution n ° 1, votre question est la suivante: Comment vous connectez-vous aux erreurs de serveur sur les sites django?

La solution la plus simple consiste à configurer la variable ADMINS avec les adresses e-mail des personnes devant recevoir des notifications d’erreur. Lorsque DEBUG = False et qu’une vue déclenche une exception, Django envoie un courrier électronique à ces personnes avec toutes les informations sur les exceptions.

settings.py

 ADMINS = (('John', '[email protected]'), ('Mary', '[email protected]')) # or only ADMINS = (('John', '[email protected]'),) 

Peut-être avez-vous besoin de EMAIL_HOST et EMAIL_PORT si le bon serveur SMTP n’est pas localhost sur le port 25 . Cette solution simple est suffisante pour tester les opérations de production, sinon elle peut produire soudainement trop d’e-mails.

Réponse courte:

Avec la configuration de la journalisation suivante, vos erreurs commenceront à apparaître dans la sortie Gunicorn (sans précision) ou le serveur d’exécution même lorsque DEBUG est défini sur False. Ils devraient de toute façon apparaître lorsque DEBUG est vrai.

 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', }, 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'formatters': { 'django.server': { '()': 'django.utils.log.ServerFormatter', 'format': '[%(server_time)s] %(message)s', } }, 'handlers': { 'console': { 'level': 'INFO', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', }, # Custom handler which we will use with logger 'django'. # We want errors/warnings to be logged when DEBUG=False 'console_on_not_debug': { 'level': 'WARNING', 'filters': ['require_debug_false'], 'class': 'logging.StreamHandler', }, 'django.server': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'django.server', }, 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' } }, 'loggers': { 'django': { 'handlers': ['console', 'mail_admins', 'console_on_not_debug'], 'level': 'INFO', }, 'django.server': { 'handlers': ['django.server'], 'level': 'INFO', 'propagate': False, }, } } 

Si vous voulez voir les erreurs de Django dans le journal des erreurs de gunicorn, lancez gunicorn avec –capture-output.

http://docs.gunicorn.org/en/stable/settings.html#capture-output

Longue réponse

Lors de la connexion, deux confusions sont en cause:

  1. Si runserver fournit un meilleur log que gunicorn
  2. Settings.DEBUG settings.DEBUG=True fournit-il un meilleur journal que settings.DEBUG=False

Tout enregistrement de journal que vous voyez avec runserver peut être vu avec Gunicorn aussi longtemps que vous avez la configuration de journalisation appropriée.

Tout enregistrement de journal que vous voyez avec DEBUG = True peut être vu alors que DEBUG = False aussi longtemps que vous avez la configuration de journalisation appropriée.

Vous pouvez voir la configuration de journalisation Django par défaut à:

https://github.com/django/django/blob/1.10.8/django/utils/log.py#L18

Il ressemble à: (j’ai enlevé des parties qui ne concernent pas cette réponse)

 DEFAULT_LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', }, 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { 'level': 'INFO', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', }, 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' } }, 'loggers': { 'django': { 'handlers': ['console', 'mail_admins'], 'level': 'INFO', }, } } 

Ce que cela dit, c’est:

  1. Envoyer l’enregistrement du journal du journal django à la console gestionnaire et à mail_admins .

  2. La console gestionnaire contient un filtre require_debug_true . Lorsque settings.DEBUG a la valeur True, la console gestionnaire envoie / imprime le journal sur le Stream (à cause de logging.StreamHandler ).

Lorsque settings.DEBUG est défini sur False, la console gestionnaire ignore alors le message de journal qui lui est envoyé par logger django .

Si vous souhaitez également que les journaux soient imprimés avec DEBUG = False, ajoutez un handler et django logger django .

Handler ressemblerait à:

  'console_on_not_debug': { 'level': 'WARNING', 'filters': ['require_debug_false'], 'class': 'logging.StreamHandler', }, 

Et utilisez ce gestionnaire avec logger django :

  'django': { 'handlers': ['console', 'mail_admins', 'console_on_not_debug'], 'level': 'INFO', }, 

Vous pouvez voir l’extrait de code en entier en bref.

Avec cela, les journaux seront imprimés sur le stream indépendamment de si vous utilisez runserver ou gunicorn.

Si vous voulez que les journaux soient affichés dans le journal des erreurs de gunicorn, vous devez exécuter gunicorn avec –capture-output.

Cette configuration a fonctionné pour moi. Ajoutez --capture-output --enable-stdio-inheritance avec la commande gunicorn comme ci-dessous.

 /home/ubuntu/inside-env/bin/gunicorn --access-logfile /var/log/access_file_g.log --error-logfile /var/log/error_file_g.log --capture-output --enable-stdio-inheritance --workers 3 --bind unix:/home/ubuntu/path-to-project/webapp.sock project.wsgi:application 

Avec cette configuration, activez la journalisation de cette manière

 import logging logging.basicConfig(level='DEBUG') logging.info('hello world') 

De cette façon, vous pourrez également voir les erreurs dans l’application.