如何使用 Gunicorn 查看 Django 错误的详细信息?

Posted

技术标签:

【中文标题】如何使用 Gunicorn 查看 Django 错误的详细信息?【英文标题】:How to see details of Django errors with Gunicorn? 【发布时间】:2014-03-23 12:43:34 【问题描述】:

我刚刚使用 gunicorn 和 nginx 部署了我的 Django (1.6) 项目。

它似乎工作正常,但我有一页是我收到 HTTP 500 错误,我无法在任何地方找到有关该错误的任何详细信息。

如何让 gunicorn 向我显示错误?

这是我当前在日志文件中看到的所有内容,当我点击页面时出现错误:

>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/

这是我用来启动 gunicorn 的 bash 脚本:

>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

更多信息:

我正在使用我使用 sudo service reports start|stop|restart 复制和修改的这个 init.d 脚本启动/停止 gunicorn:

>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

【问题讨论】:

设置 django 设置 DEBUG 为 True,然后重新加载,它应该会显示更多的错误细节 好的,我试过了,我确实在页面上看到了有帮助的错误。但我正在寻找一个更通用的解决方案,这是一个生产环境,所以我不能真正离开 debug=True 。它似乎也无助于在 gunicorn 日志文件中显示错误。 【参考方案1】:

在 Heroku 上使用 gunicorn 20.1 运行 Django 3.8,我遇到了应用程序错误未显示在 Papertrail 日志中的问题。

要解决这个问题,我只需将 Django 文档中的 minimal recommended logging configuration 添加到 settings.py:

LOGGING = 
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': 
        'console': 
            'class': 'logging.StreamHandler',
        ,
    ,
    'root': 
        'handlers': ['console'],
        'level': 'WARNING',
    ,

这是必要的,因为在生产中的Django's default logging configuration 中(当DEBUGFalse 时)django 记录器将消息发送到AdminEmailHandler,而不是控制台。

我在 Procfile 中使用 web: gunicorn my_project.wsgi 启动 gunicorn,我不需要更改它(即我不需要使用 --capture-output--enable-stdio-inheritance)。

【讨论】:

【参考方案2】:

此配置对我有用。使用 gunicorn 命令添加 --capture-output --enable-stdio-inheritance,如下所示。

/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

使用此设置,请以这种方式启用日志记录

import logging 
logging.basicConfig(level='DEBUG')

logging.info('hello world')

这样您也可以看到应用程序中的错误。

【讨论】:

被低估的答案,这就是我一直在寻找的东西。谢谢! 非常感谢,伙计。这对我来说很完美。【参考方案3】:

简答:

使用以下日志记录配置,即使 DEBUG 为 False,您的错误也会开始显示在 Gunicorn 输出(未后台处理)或运行服务器中。当 DEBUG 为 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,
    ,


如果您想在 gunicorn 错误日志中查看 Django 错误,请使用 --capture-output 运行 gunicorn。

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

长答案

记录时涉及到两个混淆:

    runserver 是否提供比gunicorn 更好的日志 settings.DEBUG=True 是否提供比settings.DEBUG=False 更好的日志

只要您有适当的日志配置,您在 runserver 中看到的任何日志记录也可以在 Gunicorn 中看到。

只要您有适当的日志记录配置,您在 DEBUG=True 时看到的任何日志记录都可以在 DEBUG=False 时看到。

您可以在以下位置查看默认的 Django 日志记录配置:

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

看起来像:(我已经删除了与此答案无关的部分)

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',
    ,


这句话是:

    django logger 日志记录发送到处理程序consolemail_admins

    处理程序console 上有一个过滤器require_debug_true。当 settings.DEBUG 为 True 时,处理程序 console 在 Stream 上发送/打印日志(因为 logging.StreamHandler)。

当 settings.DEBUG 为 False 时,处理程序 console 会忽略 logger django 发送给它的日志消息。

如果您也希望使用 DEBUG=False 打印日志,请添加 handler 并使记录器 django 使用它。

处理程序看起来像:

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

将此处理程序与记录器django一起使用:

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

您可以在简短的回答中看到整个 sn-p。

这样,无论您使用的是 runserver 还是 gunicorn,日志都将打印在流上。

如果您希望日志显示在 gunicorn 错误日志中,则需要使用 --capture-output 运行 gunicorn。

【讨论】:

【参考方案4】:

1。向控制台发送错误

这些是默认使用mail_adminsloggers(参见django/utils/log.py):

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

您需要更改处理程序以转到console,以便它出现在您的gunicorn 日志中,而不是使用mail_admins 发送电子邮件。请注意,它不像DEBUG=True那样健谈。

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

2。通过mail_admins发送错误

同样基于配置日志,显式创建一个调用mail_admins的处理程序;例如基于django/utils/log.py:

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

这需要您设置电子邮件相关的settings

3。其他解决方案

如果您不是在寻找解决方案 #1,那么您的问题与以下内容重复: How do you log server errors on django sites

【讨论】:

【参考方案5】:

最简单的解决方案是将变量 ADMINS 配置为应收到错误通知的人员的电子邮件地址。 当 DEBUG=False 并且视图引发异常时,Django 将通过电子邮件向这些人发送完整的异常信息。

settings.py

ADMINS = (('John', 'john@example.com'), ('Mary', 'mary@example.com'))
# or only ADMINS = (('John', 'john@example.com'),)

如果正确的 SMTP 服务器不是端口 25 上的 localhost,也许您还需要 EMAIL_HOST 和 EMAIL_PORT。这个简单的解决方案足以进行试产操作,否则会突然产生过多的电子邮件。

【讨论】:

【参考方案6】:

根据您的评论,我认为这是您的 django 站点中的配置问题,而不是 gunicorn 日志的问题,日志不会显示超过 django 发送给它的内容。

这是一个示例,说明如何配置 django 设置以将日志发送到您的文件(而不是默认通过电子邮件将其发送给管理员):

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,
        ,
    

阅读configuring logging(它对日志设置选项提供了很好的解释)并研究文件django/utils/log.py 以配置django loggin 以在gunicorn 日志上显示更详细。

还要检查this answer 和this,它们提供了将日志错误直接发送到文件的设置示例。并考虑使用Sentry 来处理日志错误,就像 django 人的recomended 一样。

希望这会有所帮助。

【讨论】:

哦,所以你是说新版本的 Django 默认不记录错误?在运行内置 Django 服务器时,我一直在看到回溯,所以我假设通过 Gunicorn 运行时我会得到相同的输出。 默认 ERROR 和 CRITICAL 级别通过电子邮件发送给管理员,我认为内置的 Django 开发服务器不使用此功能,因此它会在控制台中显示回溯

以上是关于如何使用 Gunicorn 查看 Django 错误的详细信息?的主要内容,如果未能解决你的问题,请参考以下文章

使用 gunicorn 时如何设置 django 测试服务器?

如何使用 gunicorn 为 django 配置 nginx?

如何将新的 django 应用程序添加到已部署的 django 项目(使用 nginx、gunicorn)?

如何调试使用 whitenoise、gunicorn 和 heroku 服务的 django 静态文件?

使用 Traefik 时如何使用 Nginx 和 Django Gunicorn 提供静态内容

使用 Gunicorn 和 nginx 部署 Django 项目