本文共 11032 字,大约阅读时间需要 36 分钟。
Django 使用Python 内建的 模块打印日志。该模块的用法在Python 本身的文档中有详细的讨论。如果你从来没有使用过Python 的logging 框架(或者即使使用过),请参见下面的快速导论。
Python 的logging 配置由四个部分组成:
Logger 为日志系统的入口。每个logger 是一个具名的容器,可以向它写入需要处理的消息。
每个logger 都有一个日志级别。日志级别表示该logger 将要处理的消息的严重性。Python 定义以下几种日志级别:
DEBUG
:用于调试目的的底层系统信息INFO
:普通的系统信息WARNING
:表示出现一个较小的问题。ERROR
:表示出现一个较大的问题。CRITICAL
:表示出现一个致命的问题。写入logger 的每条消息都是一个日志记录。每个日志记录也具有一个日志级别,它表示对应的消息的严重性。每个日志记录还可以包含描述正在打印的事件的有用元信息。这些元信息可以包含很多细节,例如回溯栈或错误码。
当给一条消息给logger 时,会将消息的日志级别与logger 的日志级别进行比较。如果消息的日志级别大于等于logger 的日志级别,该消息将会往下继续处理。如果小于,该消息将被忽略。
Logger 一旦决定消息需要处理,它将传递该消息给一个Handler。
Handler 决定如何处理logger 中的每条消息。它表示一个特定的日志行为,例如将消息写到屏幕上、写到文件中或者写到网络socket。
与logger 一样,handler 也有一个日志级别。如果消息的日志级别小于handler 的级别,handler 将忽略该消息。
Logger 可以有多个handler,而每个handler 可以有不同的日志级别。利用这种方式,可以根据消息的重要性提供不同形式的处理。例如,你可以用一个handler 将ERROR
和 CRITICAL
消息发送给一个页面服务,而用另外一个hander 将所有的消息(包括 ERROR
和CRITICAL
消息)记录到一个文件中用于以后进行分析。
Filter 用于对从logger 传递给handler 的日志记录进行额外的控制。
默认情况下,满足日志级别的任何消息都将被处理。通过安装一个filter,你可以对日志处理添加额外的条件。例如,你可以安装一个filter,只允许处理来自特定源的ERROR
消息。
Filters 还可以用于修改将要处理的日志记录的优先级。例如,如果日志记录满足特定的条件,你可以编写一个filter 将日志记录从ERROR
降为WARNING
。
Filters 可以安装在logger 上或者handler 上;多个filter 可以串联起来实现多层filter 行为。
最后,日志记录需要转换成文本。Formatter 表示文本的格式。Fomatter 通常由包含的Python 格式字符串组成;你也可以编写自定义的fomatter 来实现自己的格式。
配置好logger、handler、filter 和formatter 之后,你需要在代码中放入logging 调用。使用logging 框架非常简单。下面是个例子:
# import the logging libraryimport logging# Get an instance of a loggerlogger = logging.getLogger(__name__)def my_view(request, arg1, arg): ... if bad_mojo: # Log an error message logger.error('Something went wrong!')
就是这样!每次满足bad_mojo
条件,将写入一条错误日志记录。
调用获取(如有必要则创建)一个logger 的实例。Logger 实例通过名字标识。Logger 使用名称的目的是用于标识其配置。
Logger 的名称习惯上通常使用__name__
,即包含该logger 的Python 模块的名字。这允许你基于模块filter 和handle 日志调用。如果你想使用其它方式组织日志消息,可以提供点号分隔的名称来标识你的logger:
# Get an instance of a specific named loggerlogger = logging.getLogger('project.interesting.stuff')
点号分隔的logger 名称定义一个层级。project.interesting
logger 被认为是 project.interesting.stuff
logger 的上一级;project
logger 是project.interesting
logger 的上一级。
层级为何如此重要?因为可以设置logger 传播它们的logging 调用给它们的上一级。利用这种方式,你可以在根logger 上定义一系列的handler,并捕获子logger 中的所有logging 调用。在project
命名空间中定义的handler 将捕获project.interesting
和project.interesting.stuff
logger 上的所有日志消息。
这种传播行为可以基于每个logger 进行控制。如果你不想让某个logger 传播消息给它的上一级,你可以关闭这个行为。
Logger 实例为每个默认的日志级别提供一个入口方法:
logger.debug()
logger.info()
logger.warning()
logger.error()
logger.critical()
还有另外两个调用:
logger.log()
:打印消息时手工指定日志级别。logger.exception()
:创建一个ERROR
级别日志消息,它封装当前异常栈的帧。当然,只是将logging 调用放入你的代码中还是不够的。你还需要配置logger、handler、filter 和formatter 来确保日志的输出是有意义的。
Python 的logging 库提供几种配置logging 的技术,从程序接口到配置文件。默认情况下,Django 使用。
为了配置logging,你需要使用 来定义字典形式的logging 设置。这些设置描述你的logging 设置的logger、handler、filter 和formatter,以及它们的日志等级和其它属性。
默认情况下, 设置与进行合并。
如果 中的disable_existing_loggers
键为True
(默认值),那么默认配置中的所有logger 都将禁用。Logger 的禁用与删除不同;logger 仍然存在,但是将默默丢弃任何传递给它的信息,也不会传播给上一级logger。所以,你应该非常小心使用'disable_existing_loggers': True
;它可能不是你想要的。你可以设置disable_existing_loggers
为False
,并重新定义部分或所有的默认loggers;或者你可以设置 为 None
,并 。
Logging 的配置属于Django setup()
函数的一部分。所以,你可以肯定在你的项目代码中logger 是永远可用的。
的完整文档是logging 字典配置最好的信息源。但是为了让你尝尝,下面是几个例子。
首先,下面是一个简单的配置,它将来自 logger 的所有日志请求写入到一个本地文件:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': '/path/to/django/debug.log', }, }, 'loggers': { 'django.request': { 'handlers': ['file'], 'level': 'DEBUG', 'propagate': True, }, },}
如果你使用这个示例,请确保修改'filename'
路径为运行Django 应用的用户有权限写入的一个位置。
其次,下面这个示例演示如何让日志系统将Django 的日志打印到控制台。django.request
和django.security
不会传播日志给上一级。它在本地开发期间可能有用。
默认情况下,这个配置只会将INFO
和更高级别的日志发送到控制台。Django 中这样的日志信息不多。可以设置环境变量DJANGO_LOG_LEVEL=DEBUG
来看看Django 的debug 日志,它包含所有的数据库查询所以非常详尽。
import osLOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django': { 'handlers': ['console'], 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), }, },}
最后,下面是相当复杂的一个logging 设置:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' }, 'simple': { 'format': '%(levelname)s %(message)s' }, }, 'filters': { 'special': { '()': 'project.logging.SpecialFilter', 'foo': 'bar', } }, 'handlers': { 'null': { 'level': 'DEBUG', 'class': 'logging.NullHandler', }, 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'filters': ['special'] } }, 'loggers': { 'django': { 'handlers': ['null'], 'propagate': True, 'level': 'INFO', }, 'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, 'myproject.custom': { 'handlers': ['console', 'mail_admins'], 'level': 'INFO', 'filters': ['special'] } }}
这个logging 配置完成以下事情:
以‘dictConfig version 1’格式解析配置。目前为止,这是dictConfig 格式唯一的版本。
定义两个formatter:
simple
,它只输出日志的级别(例如,DEBUG
)和日志消息。
format
字符串是一个普通的Python 格式化字符串,描述每行日志的细节。输出的完整细节可以在中找到。
verbose
,它输出日志级别、日志消息,以及时间、进程、线程和生成日志消息的模块。
定义filter —— project.logging.SpecialFilter
,并使用别名special
。如果filter 在构造时要求额外的参数,可以在filter 的配置字段中用额外的键提供。在这个例子中,在实例化SpecialFilter
时,foo
参数的值将使用bar
。
定义三个handler:
null
,一个NullHandler,它传递DEBUG
(和更高级)的消息给/dev/null
。console
,一个StreamHandler,它将打印DEBUG
(和更高级)的消息到stderr。这个handler 使用simple
输出格式。mail_admins
,一个AdminEmailHandler,它将用邮件发送ERROR
(和更高级)的消息到站点管理员。这个handler 使用special
filter。配置三个logger:
django
,它传递所有INFO
和更高级的消息给null
handler。django.request
,它传递所有ERROR
消息给mail_admins
handler。另外,标记这个logger 不 向上传播消息。这表示写入django.request
的日志信息将不会被django
logger 处理。myproject.custom
,它传递所有INFO
和更高级的消息并通过special
filter 的消息给两个handler —— console
和mail_admins
。这表示所有INFO
(和更高级)的消息将打印到控制台上;ERROR
和CRITICAL
消息还会通过邮件发送出来。如果你不想使用Python 的dictConfig 格式配置logger,你可以指定你自己的配置模式。
设置定义一个可调用对象,将它用来配置Django 的logger。默认情况下,它指向Python 的 函数。但是,如果你想使用不同的配置过程,你可以使用其它只接受一个参数的可调用对象。配置logging 时,将使用 的内容作为参数的值。
如果你完全不想配置logging(或者你想使用自己的方法手工配置logging),你可以设置 为None
。这将禁用 的配置过程。下面的示例禁用Django 的logging 配置,然后手工配置logging:
settings.py
LOGGING_CONFIG = Noneimport logging.configlogging.config.dictConfig(...)
设置 为None
只表示禁用自动配置过程,而不是禁用logging 本身。如果你禁用配置过程,Django 仍然执行logging 调用,只是调用的是默认定义的logging 行为。
Django 提供许多工具用于处理在网站服务器环境中独特的日志需求。
Django 提供几个内建的logger。
django
是一个捕获所有信息的logger。消息不会直接提交给这个logger。
记录与处理请求相关的消息。5XX 响应作为ERROR
消息;4XX 响应作为WARNING
消息。
这个logger 的消息具有以下额外的上下文:
status_code
:请求的HTTP 响应码。request
:生成日志信息的请求对象。与数据库交互的代码相关的消息。例如,HTTP请求执行应用级别的SQL 语句将以DEBUG
级别记录到该logger。
这个logger 的消息具有以下额外的上下文:
duration
:执行SQL 语句花费的时间。sql
:执行的SQL 语句。params
:SQL 调用中用到的参数。由于性能原因,SQL的日志只在设置
之后开启。DEBUG 设置为True
,无论日志级别或者安装的处理器是什么。
这里的日志不包含框架级别的的初始化(例如,SET TIMEZONE
)和事务管理查询(例如,BEGIN
、COMMIT
和ROLLBACK
)。如果你希望看到所有的数据库查询,可以打开数据库中的查询日志。
Security logger 将收到任何出现 的消息。SuspiciousOperation 的每个子类型都有一个子logger。日志的级别取决于异常处理的位置。大部分情况是一个warning 日志,而如果SuspiciousOperation
到达WSGI handler 则记录为一个error。例如,如果请求中包含的HTTP Host
头部与 不匹配,Django 将返回400 响应,同时将记录一个error 消息到django.security.DisallowedHost
logger。
默认情况下只会配置django.security
logger,其它所有的子logger 都将传播给上一级logger。django.security
logger 的配置与django.request
logger 相同,任何error 消息将用邮件发送给站点管理员。由于SuspiciousOperation
导致400 响应的请求不会在django.request
logger 中记录日志,而只在django.security
logger 中记录日志。
若要默默丢弃某种类型的SuspiciousOperation,你可以按照下面的示例覆盖其logger:
'loggers': { 'django.security.DisallowedHost': { 'handlers': ['null'], 'propagate': False, },},
New in Django 1.7.
当执行的SQL 查询会改变数据库的模式时,则记录这些SQL 查询。注意,它不会记录 执行的查询。
在Python logging 模块提供的handler 基础之上,Django 还提供另外一个handler。
class AdminEmailHandler
(include_html=False, email_backend=None)
这个handler 将它收到的每个日志信息用邮件发送给站点管理员。
如果日志记录包含request
属性,该请求的完整细节都将包含在邮件中。
如果日志记录包含栈回溯信息,该栈回溯也将包含在邮件中。
AdminEmailHandler
的include_html
参数用于控制邮件中是否包含HTML 附件,这个附件包含 为True
时的完整网页。若要在配置中设置这个值,可以将它包含在django.utils.log.AdminEmailHandler
handler 的定义中,像下面这样:
'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'include_html': True, }},
注意,邮件中的HTML 包含完整的回溯栈,包括栈每个层级局部变量的名称和值以及你的Django 设置。这些信息可能非常敏感,你也许不想通过邮件发送它们。此时可以考虑使用类似 这样的东西,回溯栈的完整信息和安全信息不会 通过邮件发送。你还可以从错误报告中显式过滤掉特定的敏感信息 —— 更多信息参见。
通过设置AdminEmailHandler
的email_backend
参数,可以覆盖handler 使用的,像这样:
'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'email_backend': 'django.core.mail.backends.filebased.EmailBackend', }},
默认情况下,将使用 中指定的邮件后端。
send_mail
(subject, message, *args, **kwargs)
New in Django 1.8.
发送邮件给管理员用户。若要自定它的行为,可以子类化 类并覆盖这个方法。
在Python logging 模块提供的过滤器的基础之上,Django 还提供两个过滤器。
class CallbackFilter
(callback)
这个过滤器接受一个回调函数(它接受一个单一参数,也就是要记录的东西),并且对每个传递给过滤器的记录调用它。如果回调函数返回False,将不会进行记录的处理。
例如,要从admin邮件中过滤掉(只在用户取消上传时产生),你可以创建一个过滤器函数:
from django.http import UnreadablePostErrordef skip_unreadable_post(record): if record.exc_info: exc_type, exc_value = record.exc_info[:2] if isinstance(exc_value, UnreadablePostError): return False return True
然后把它添加到logger的配置中:
'filters': { 'skip_unreadable_posts': { '()': 'django.utils.log.CallbackFilter', 'callback': skip_unreadable_post, }},'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['skip_unreadable_posts'], 'class': 'django.utils.log.AdminEmailHandler' }},
class RequireDebugFalse
这个过滤器只在设置后传递记录。DEBUG 为 False。
这个过滤器遵循 默认的配置,以确保只在为False
的时候发送错误邮件。
'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', }},'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' }},
class RequireDebugTrue
这个过滤器类似于,除了记录只在 为 True
时传递的情况。
默认情况下,Django 的logging 配置如下:
当 为True
时:
django
的全局logger会向控制台发送级别等于或高级INFO
的所有消息。Django在这个时候并不会做任何日志调用(所有在DEBUG
级别上的日志,或者被django.request
和 django.security
处理的日志)。py.warnings
logger,它处理来自warnings.warn()
的消息,会向控制台发送消息。当 为False
时:
django.request
和django.security
loggers 向发送带有ERROR
或 CRITICAL
级别的消息。这些logger 会忽略任何级别等于或小于WARNING
的信息,被记录的日志不会传递给其他logger(它们不会传递给django
的全局 logger,即使DEBUG
为 True
)。另见来了解如何补充或者替换默认的日志配置。
译者:,原文:。
本文以 协议发布,转载请保留作者署名和文章出处。
人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。