一直以来都有一个疑惑,django在 DEBUG = True
时,程序出错时会把错误的堆栈信息打印到控制台,然而当 DEBUG = False
时,程序异常只是显示一条状态码为500 的记录,而堆栈信息不会显示。
[11/Apr/2020 23:10:19] "GET /api/v1/course/1/ HTTP/1.1" 500 145
由于服务器上一般用 uwsgi 来部署,所以 uwsgi 拿到的日志即 Django 的控制台输出日志,因此应该只需要修改 Django 控制台输出的日志,uwsgi 就能拿到的异常时的堆栈信息了。
在查看 StackOverflow 的这条问题 How do you log server errors on Django sites 后,发现有两种方法可以实现错误信息的堆栈输出。
方法一:通过中间件处理异常
在项目中创建一个py文件(名字随便),我这里创建在 utils/middleware.py
,并创建一个类通过 process_exception
方法处理异常
# utils/middleware.py
import logging
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class ErrorMiddleware(MiddlewareMixin):
def process_exception(self, request, exception):
if not settings.DEBUG:
logging.exception(exception)
在 settings.py
添加该中间件
# settings.py
DEBUG = False
MIDDLEWARE = [
......
'utils.middleware.ErrorMiddleware', # 这是你写的类的导入路径
]
方法二:修改 Django 的 log 日志配置
Django 的 log 默认配置如下
# django\utils\log.py
DEFAULT_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}] {message}',
'style': '{',
}
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'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'],
'level': 'INFO',
},
'django.server': {
'handlers': ['django.server'],
'level': 'INFO',
'propagate': False,
},
}
}
查看 Django 的 log 配置发现,原来控制台输出的日志时做了过滤,因此最简单的方法就是把 handlers
中 console
的 filters
注释了就可以,因此在 settings.py
文件下添加一下配置:
# settings.py
DEFAULT_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}] {message}',
'style': '{',
}
},
'handlers': {
'console': {
'level': 'INFO',
# 'filters': ['require_debug_true'], # 把该条件注释,使得debug不管True或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'],
'level': 'INFO',
},
'django.server': {
'handlers': ['django.server'],
'level': 'INFO',
'propagate': False,
},
}
}
根据上面的配置,还可以进行自定义,把日志写入到本地,配置如下
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
# Include the default Django email handler for errors
# This is what you'd get without configuring logging at all.
'mail_admins': {
'class': 'django.utils.log.AdminEmailHandler',
'level': 'ERROR',
# But the emails are plain text by default - HTML is nicer
'include_html': True,
},
# Log to a text file that can be rotated by logrotate
'logfile': {
'class': 'logging.handlers.WatchedFileHandler',
'filename': '/var/log/django/myapp.log'
},
},
'loggers': {
# Again, default Django configuration to email unhandled exceptions
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
# Might as well log any errors anywhere else in Django
'django': {
'handlers': ['logfile'],
'level': 'ERROR',
'propagate': False,
},
# Your own app - this assumes all your logger names start with "myapp."
'myapp': {
'handlers': ['logfile'],
'level': 'WARNING', # Or maybe INFO or DEBUG
'propagate': False
},
},
}
参考:
网友评论