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

时间:2014-02-21 19:53:49

标签: django bash nginx gunicorn django-errors

我刚刚使用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

5 个答案:

答案 0 :(得分:14)

从你的评论我认为这是你的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 answerthis,它们提供了将日志错误直接发送到文件的设置示例。并且考虑使用Sentry来处理日志错误,django人也是recomended

希望这有帮助。

答案 1 :(得分:7)

1。将错误发送到控制台

默认情况下使用loggers的{​​{1}}就是mail_admins

django/utils/log.py

您需要更改处理程序以转到 'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, 'django.security': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, ,以便它显示在您的gunicorn日志中,而不是使用console发送电子邮件。请注意,它不像mail_admins时那么健谈。

DEBUG=True

2。通过 'loggers': { 'django': { 'level': 'ERROR', 'handlers': ['console'], }, }

发送错误

同样基于配置日志记录,显式创建一个调用mail_admins的处理程序;例如基于mail_admins

django/utils/log.py

这需要您设置与电子邮件相关的 'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler' }, }, 'loggers': { 'django': { 'handlers': ['mail_admins'], }, }

3。其他解决方案

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

答案 2 :(得分:4)

简答:

使用以下日志记录配置,即使DEBUG为False,您的错误也会开始显示在Gunicorn输出(undaemonized)或runserver中。当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

答案很长

记录时涉及两个混淆:

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

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

    只要您有适当的日志记录配置,就可以看到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',
        },
    }
    }
    

    这说的是:

    1. django记录器日志记录发送给处理程序consolemail_admins

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

    3. 当settings.DEBUG为False时,处理程序console将忽略记录器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',
          },
      

      您可以在简短的回答中看到整个代码段。

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

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

答案 3 :(得分:2)

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

settings.py

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

如果端口localhost上的正确SMTP服务器不是25,则可能还需要EMAIL_HOST和EMAIL_PORT。这个简单的解决方案对于试生产操作来说足够好,否则会突然产生过多的电子邮件。

答案 4 :(得分:0)

此配置对我有用。像下面这样用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')

这样,您也可以在应用程序中查看错误。