Je viens de déployer mon projet Django (1.6) avec gunicorn et Nginx.
Cela semble fonctionner correctement, mais j'ai une page où je reçois une erreur HTTP 500 et je ne trouve aucun détail sur l'erreur n'importe où.
Comment faire pour que gunicorn me montre des erreurs?
Voici tout ce que je vois actuellement dans le fichier journal lorsque je clique sur 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 démarrer 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
De votre commentaire, je pense que ceci est un problème de configuration dans votre site Django, pas une question de journal gunicorn, les journaux ne montreront pas plus que Django lui envoyer.
Voici un exemple de la façon dont vous pouvez configurer le paramètre Django pour envoyer le journal dans votre fichier (au lieu de l'envoyer aux administrateurs par courrier électronique 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 configurez la journalisation (il fournit une très bonne explication des options des paramètres de journalisation) et étudiez le fichier Django/utils/log.py pour configurer la journalisation Django afin qu’elle apparaisse plus en détail dans les journaux de Gunicorn.
Vérifiez également cette réponse et ceci qui fournissent des exemples de configuration pour envoyer les erreurs de journal directement dans un fichier. Et envisagez d'utiliser Sentry pour gérer les erreurs de journalisation, tel que recommandé par Django.
J'espère que cela t'aides.
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 auriez besoin de changer les gestionnaires pour accéder à la variable console
afin qu'elle apparaisse dans votre journal des armes à feu plutôt que d'envoyer des courriels avec mail_admins
. S'il vous plaît noter que ce n'est pas aussi bavard que lorsque DEBUG=True
.
'loggers': {
'Django': {
'level': 'ERROR',
'handlers': ['console'],
},
}
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'],
},
}
Pour cela, vous devez définir une variable settings
liée au courrier électronique.
Si vous ne cherchiez pas la solution 1, votre question est une copie de: Comment consignez-vous les erreurs de serveur sur les sites Django
Avec la configuration de la journalisation suivante, vos erreurs commenceront à apparaître dans la sortie Gunicorn (undaemonized) ou le serveur d'exécution, même lorsque DEBUG est défini sur False. Ils devraient quand même apparaître lorsque DEBUG est True.
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 Django dans le journal des erreurs gunicorn, exécutez gunicorn avec --capture-output.
http://docs.gunicorn.org/fr/stable/settings.html#capture-output
Il existe deux confusions lors de la journalisation:
runserver
fournit un meilleur journal que gunicorn
settings.DEBUG=True
fournit-il un meilleur journal que settings.DEBUG=False
L'enregistrement de journal Any que vous voyez avec le serveur d'exécution peut également être vu avec Gunicorn, à condition que la configuration de la journalisation soit appropriée.
_ {Tout enregistrement de journal que vous voyez avec DEBUG = True peut être vu pendant que DEBUG = False aussi, tant que vous avez la configuration de journalisation appropriée.
Vous pouvez voir la configuration de journalisation Django par défaut sur:
https://github.com/Django/django/blob/1.10.8/Django/utils/log.py#L18
Cela 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:
Envoyez l'enregistrement de journal du journal Django
aux gestionnaires console
et mail_admins
.
Le gestionnaire console
a un filtre require_debug_true
sur celui-ci. Lorsque settings.DEBUG a la valeur True, le gestionnaire console
envoie/imprime le journal sur le flux (en raison de logging.StreamHandler
).
Lorsque settings.DEBUG a la valeur False, le gestionnaire console
ignore le message de journal qui lui a été envoyé par le consignateur Django
.
Si vous souhaitez également que les journaux soient imprimés avec DEBUG = False, ajoutez une handler
et faites en sorte que logger Django
l'utilise.
Le gestionnaire ressemblerait à:
'console_on_not_debug': {
'level': 'WARNING',
'filters': ['require_debug_false'],
'class': 'logging.StreamHandler',
},
Et utilisez ce gestionnaire avec le logger Django
:
'Django': {
'handlers': ['console', 'mail_admins', 'console_on_not_debug'],
'level': 'INFO',
},
Vous pouvez voir l'extrait entier en réponse courte.
Avec cela, les journaux seront imprimés en continu, que vous utilisiez ou non RunServer ou Gunicorn.
Si vous voulez que les journaux soient affichés dans le journal des erreurs gunicorn, vous devez exécuter gunicorn avec --capture-output.
La solution la plus simple consiste à configurer la variable ADMINISTRATEURS avec les adresses électroniques des personnes qui devraient recevoir des notifications d'erreur . Lorsque DEBUG = False et qu'une vue lève une exception, Django enverra un courrier électronique à ces personnes avec les informations complètes sur les exceptions.
settings.py
ADMINS = (('John', '[email protected]'), ('Mary', '[email protected]'))
# or only ADMINS = (('John', '[email protected]'),)
Peut-être avez-vous également besoin de EMAIL_Host et de EMAIL_PORT si le bon serveur SMTP n'est pas localhost
sur le port 25
. Cette solution simple convient assez bien pour les essais de production, sinon elle risque de produire soudainement trop de courriels.
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.