# 日志
## 日志快速入门
Django 使用Python 内建的[`logging`](https://docs.python.org/3/library/logging.html#module-logging) 模块打印日志。该模块的用法在Python 本身的文档中有详细的讨论。如果你从来没有使用过Python 的logging 框架(或者即使使用过),请参见下面的快速导论。
### logging 的组成
Python 的logging 配置由四个部分组成:
* [Loggers](#topic-logging-parts-loggers)
* [Handlers](#topic-logging-parts-handlers)
* [Filters](#topic-logging-parts-filters)
* [Formatters](#topic-logging-parts-formatters)
#### Loggers
Logger 为日志系统的入口。每个logger 是一个具名的容器,可以向它写入需要处理的消息。
每个logger 都有一个_日志级别_。日志级别表示该logger 将要处理的消息的严重性。Python 定义以下几种日志级别:
* `DEBUG`:用于调试目的的底层系统信息
* `INFO`:普通的系统信息
* `WARNING`:表示出现一个较小的问题。
* `ERROR`:表示出现一个较大的问题。
* `CRITICAL`:表示出现一个致命的问题。
写入logger 的每条消息都是一个_日志记录_。每个日志记录也具有一个_日志级别_,它表示对应的消息的严重性。每个日志记录还可以包含描述正在打印的事件的有用元信息。这些元信息可以包含很多细节,例如回溯栈或错误码。
当给一条消息给logger 时,会将消息的日志级别与logger 的日志级别进行比较。如果消息的日志级别大于等于logger 的日志级别,该消息将会往下继续处理。如果小于,该消息将被忽略。
Logger 一旦决定消息需要处理,它将传递该消息给一个_Handler_。
#### Handlers
Handler 决定如何处理logger 中的每条消息。它表示一个特定的日志行为,例如将消息写到屏幕上、写到文件中或者写到网络socket。
与logger 一样,handler 也有一个日志级别。如果消息的日志级别小于handler 的级别,handler 将忽略该消息。
Logger 可以有多个handler,而每个handler 可以有不同的日志级别。利用这种方式,可以根据消息的重要性提供不同形式的处理。例如,你可以用一个handler 将`ERROR` 和 `CRITICAL` 消息发送给一个页面服务,而用另外一个hander 将所有的消息(包括 `ERROR` 和`CRITICAL` 消息)记录到一个文件中用于以后进行分析。
#### Filters
Filter 用于对从logger 传递给handler 的日志记录进行额外的控制。
默认情况下,满足日志级别的任何消息都将被处理。通过安装一个filter,你可以对日志处理添加额外的条件。例如,你可以安装一个filter,只允许处理来自特定源的`ERROR` 消息。
Filters 还可以用于修改将要处理的日志记录的优先级。例如,如果日志记录满足特定的条件,你可以编写一个filter 将日志记录从`ERROR` 降为`WARNING`。
Filters 可以安装在logger 上或者handler 上;多个filter 可以串联起来实现多层filter 行为。
#### Formatters
最后,日志记录需要转换成文本。Formatter 表示文本的格式。Fomatter 通常由包含[_日志记录属性_](https://docs.python.org/3/library/logging.html#logrecord-attributes)的Python 格式字符串组成;你也可以编写自定义的fomatter 来实现自己的格式。
## 使用logging
配置好logger、handler、filter 和formatter 之后,你需要在代码中放入logging 调用。使用logging 框架非常简单。下面是个例子:
```
# import the logging library
import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
def my_view(request, arg1, arg):
...
if bad_mojo:
# Log an error message
logger.error('Something went wrong!')
```
就是这样!每次满足`bad_mojo` 条件,将写入一条错误日志记录。
### 命名logger
[`logging.getLogger()`](https://docs.python.org/3/library/logging.html#logging.getLogger) 调用获取(如有必要则创建)一个logger 的实例。Logger 实例通过名字标识。Logger 使用名称的目的是用于标识其配置。
Logger 的名称习惯上通常使用`__name__`,即包含该logger 的Python 模块的名字。这允许你基于模块filter 和handle 日志调用。如果你想使用其它方式组织日志消息,可以提供点号分隔的名称来标识你的logger:
```
# Get an instance of a specific named logger
logger = 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 传播消息给它的上一级,你可以关闭这个行为。
### logging 调用
Logger 实例为每个默认的日志级别提供一个入口方法:
* `logger.debug()`
* `logger.info()`
* `logger.warning()`
* `logger.error()`
* `logger.critical()`
还有另外两个调用:
* `logger.log()`:打印消息时手工指定日志级别。
* `logger.exception()`:创建一个`ERROR` 级别日志消息,它封装当前异常栈的帧。
## 配置logging
当然,只是将logging 调用放入你的代码中还是不够的。你还需要配置logger、handler、filter 和formatter 来确保日志的输出是有意义的。
Python 的logging 库提供几种配置logging 的技术,从程序接口到配置文件。默认情况下,Django 使用[dictConfig 格式](https://docs.python.org/library/logging.config.html#configuration-dictionary-schema)。
为了配置logging,你需要使用[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 来定义字典形式的logging 设置。这些设置描述你的logging 设置的logger、handler、filter 和formatter,以及它们的日志等级和其它属性。
默认情况下,[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 设置与[_Django 的默认logging 配置_](#default-logging-configuration)进行合并。
如果[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 中的`disable_existing_loggers` 键为`True`(默认值),那么默认配置中的所有logger 都将禁用。Logger 的禁用与删除不同;logger 仍然存在,但是将默默丢弃任何传递给它的信息,也不会传播给上一级logger。所以,你应该非常小心使用`'disable_existing_loggers': True`;它可能不是你想要的。你可以设置`disable_existing_loggers` 为`False`,并重新定义部分或所有的默认loggers;或者你可以设置[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 为 `None`,并 [_自己处理logging 配置_](#disabling-logging-configuration)。
Logging 的配置属于Django `setup()` 函数的一部分。所以,你可以肯定在你的项目代码中logger 是永远可用的。
### 示例
[dictConfig 格式](https://docs.python.org/library/logging.config.html#configuration-dictionary-schema)的完整文档是logging 字典配置最好的信息源。但是为了让你尝尝,下面是几个例子。
首先,下面是一个简单的配置,它将来自[_django.request_](#django-request-logger) 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 os
LOGGING = {
'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 格式化字符串,描述每行日志的细节。输出的完整细节可以在[formatter 文档](https://docs.python.org/library/logging.html#formatter-objects)中找到。
* `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` 消息还会通过邮件发送出来。
### 自定义logging 配置
如果你不想使用Python 的dictConfig 格式配置logger,你可以指定你自己的配置模式。
[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 设置定义一个可调用对象,将它用来配置Django 的logger。默认情况下,它指向Python 的[`logging.config.dictConfig()`](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig) 函数。但是,如果你想使用不同的配置过程,你可以使用其它只接受一个参数的可调用对象。配置logging 时,将使用[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 的内容作为参数的值。
### 禁用logging 配置
如果你完全不想配置logging(或者你想使用自己的方法手工配置logging),你可以设置[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 为`None`。这将禁用[_Django 默认logging_](#default-logging-configuration) 的配置过程。下面的示例禁用Django 的logging 配置,然后手工配置logging:
settings.py
```
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
```
设置[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 为`None` 只表示禁用自动配置过程,而不是禁用logging 本身。如果你禁用配置过程,Django 仍然执行logging 调用,只是调用的是默认定义的logging 行为。
## Django’s logging extensions
Django 提供许多工具用于处理在网站服务器环境中独特的日志需求。
### Loggers
Django 提供几个内建的logger。
#### django
`django` 是一个捕获所有信息的logger。消息不会直接提交给这个logger。
#### django.request
记录与处理请求相关的消息。5XX 响应作为`ERROR` 消息;4XX 响应作为`WARNING` 消息。
这个logger 的消息具有以下额外的上下文:
* `status_code`:请求的HTTP 响应码。
* `request`:生成日志信息的请求对象。
#### django.db.backends
与数据库交互的代码相关的消息。例如,HTTP请求执行应用级别的SQL 语句将以`DEBUG` 级别记录到该logger。
这个logger 的消息具有以下额外的上下文:
* `duration`:执行SQL 语句花费的时间。
* `sql`:执行的SQL 语句。
* `params`:SQL 调用中用到的参数。
由于性能原因,SQL的日志只在`设置`之后开启。DEBUG 设置为`True`,无论日志级别或者安装的处理器是什么。
这里的日志不包含框架级别的的初始化(例如,`SET TIMEZONE`)和事务管理查询(例如,`BEGIN`、`COMMIT` 和`ROLLBACK`)。如果你希望看到所有的数据库查询,可以打开数据库中的查询日志。
#### django.security.*
Security logger 将收到任何出现[`SuspiciousOperation`](../ref/exceptions.html#django.core.exceptions.SuspiciousOperation "django.core.exceptions.SuspiciousOperation") 的消息。SuspiciousOperation 的每个子类型都有一个子logger。日志的级别取决于异常处理的位置。大部分情况是一个warning 日志,而如果`SuspiciousOperation` 到达WSGI handler 则记录为一个error。例如,如果请求中包含的HTTP `Host` 头部与[`ALLOWED_HOSTS`](../ref/settings.html#std:setting-ALLOWED_HOSTS) 不匹配,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,
},
},
```
#### django.db.backends.schema
New in Django 1.7\.
当[_迁移框架_](migrations.html)执行的SQL 查询会改变数据库的模式时,则记录这些SQL 查询。注意,它不会记录[`RunPython`](../ref/migration-operations.html#django.db.migrations.operations.RunPython "django.db.migrations.operations.RunPython") 执行的查询。
### Handlers
在Python logging 模块提供的handler 基础之上,Django 还提供另外一个handler。
_class _`AdminEmailHandler`(_include_html=False_, _email_backend=None_)[[source]](../_modules/django/utils/log.html#AdminEmailHandler)
这个handler 将它收到的每个日志信息用邮件发送给站点管理员。
如果日志记录包含`request` 属性,该请求的完整细节都将包含在邮件中。
如果日志记录包含栈回溯信息,该栈回溯也将包含在邮件中。
`AdminEmailHandler` 的`include_html` 参数用于控制邮件中是否包含HTML 附件,这个附件包含[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 为`True` 时的完整网页。若要在配置中设置这个值,可以将它包含在`django.utils.log.AdminEmailHandler` handler 的定义中,像下面这样:
```
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
}
},
```
注意,邮件中的HTML 包含完整的回溯栈,包括栈每个层级局部变量的名称和值以及你的Django 设置。这些信息可能非常敏感,你也许不想通过邮件发送它们。此时可以考虑使用类似[Sentry](https://pypi.python.org/pypi/sentry) 这样的东西,回溯栈的完整信息和安全信息_不会_ 通过邮件发送。你还可以从错误报告中显式过滤掉特定的敏感信息 —— 更多信息参见[_过滤错误报告_](../howto/error-reporting.html#filtering-error-reports)。
通过设置`AdminEmailHandler` 的`email_backend` 参数,可以覆盖handler 使用的[_email backend_](email.html#topic-email-backends),像这样:
```
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
}
},
```
默认情况下,将使用[`EMAIL_BACKEND`](../ref/settings.html#std:setting-EMAIL_BACKEND) 中指定的邮件后端。
`send_mail`(_subject_, _message_, _*args_, _**kwargs_)[[source]](../_modules/django/utils/log.html#AdminEmailHandler.send_mail)
New in Django 1.8\.
发送邮件给管理员用户。若要自定它的行为,可以子类化[`AdminEmailHandler`](#django.utils.log.AdminEmailHandler "django.utils.log.AdminEmailHandler") 类并覆盖这个方法。
### Filters
在Python logging 模块提供的过滤器的基础之上,Django 还提供两个过滤器。
_class _`CallbackFilter`(_callback_)[[source]](../_modules/django/utils/log.html#CallbackFilter)
这个过滤器接受一个回调函数(它接受一个单一参数,也就是要记录的东西),并且对每个传递给过滤器的记录调用它。如果回调函数返回False,将不会进行记录的处理。
例如,要从admin邮件中过滤掉[`UnreadablePostError`](../ref/exceptions.html#django.http.UnreadablePostError "django.http.UnreadablePostError")(只在用户取消上传时产生),你可以创建一个过滤器函数:
```
from django.http import UnreadablePostError
def 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`[[source]](../_modules/django/utils/log.html#RequireDebugFalse)
这个过滤器只在设置后传递记录。DEBUG 为 False。
这个过滤器遵循[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 默认的配置,以确保[`AdminEmailHandler`](#django.utils.log.AdminEmailHandler "django.utils.log.AdminEmailHandler")只在[`DEBUG`](../ref/settings.html#std:setting-DEBUG)为`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`[[source]](../_modules/django/utils/log.html#RequireDebugTrue)
这个过滤器类似于[`RequireDebugFalse`](#django.utils.log.RequireDebugFalse "django.utils.log.RequireDebugFalse"),除了记录只在[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 为 `True`时传递的情况。
## Django’s default logging configuration
默认情况下,Django 的logging 配置如下:
当[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 为`True` 时:
* `django`的全局logger会向控制台发送级别等于或高级`INFO`的所有消息。Django在这个时候并不会做任何日志调用(所有在`DEBUG`级别上的日志,或者被`django.request` 和 `django.security`处理的日志)。
* `py.warnings` logger,它处理来自`warnings.warn()`的消息,会向控制台发送消息。
当[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 为`False` 时:
* `django.request` 和`django.security` loggers 向[`AdminEmailHandler`](#django.utils.log.AdminEmailHandler "django.utils.log.AdminEmailHandler")发送带有`ERROR` 或 `CRITICAL`级别的消息。这些logger 会忽略任何级别等于或小于`WARNING`的信息,被记录的日志不会传递给其他logger(它们不会传递给`django`的全局 logger,即使`DEBUG` 为 `True`)。
另见[_配置日志_](#configuring-logging)来了解如何补充或者替换默认的日志配置。
> 译者:[Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html),原文:[Logging](https://docs.djangoproject.com/en/1.8/topics/logging/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 协议发布,转载请保留作者署名和文章出处。
>
> [Django 文档协作翻译小组](http://python.usyiyi.cn/django/index.html)人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。
- 新手入门
- 从零开始
- 概览
- 安装
- 教程
- 第1部分:模型
- 第2部分:管理站点
- 第3部分:视图和模板
- 第4部分:表单和通用视图
- 第5部分:测试
- 第6部分:静态文件
- 高级教程
- 如何编写可重用的应用
- 为Django编写首个补丁
- 模型层
- 模型
- 模型语法
- 元选项
- 模型类
- 查询集
- 执行查询
- 查找表达式
- 模型的实例
- 实例方法
- 访问关联对象
- 迁移
- 模式编辑器
- 编写迁移
- 高级
- 管理器
- 原始的SQL查询
- 聚合
- 多数据库
- 自定义查找
- 条件表达式
- 数据库函数
- 其它
- 遗留的数据库
- 提供初始数据
- 优化数据库访问
- 视图层
- 基础
- URL配置
- 视图函数
- 快捷函数
- 装饰器
- 参考
- 内建的视图
- TemplateResponse 对象
- 文件上传
- 概览
- File 对象
- 储存API
- 管理文件
- 自定义存储
- 基于类的视图
- 概览
- 内建显示视图
- 内建编辑视图
- API参考
- 分类索引
- 高级
- 生成 CSV
- 生成 PDF
- 中间件
- 概览
- 内建的中间件类
- 模板层
- 基础
- 面向设计师
- 语言概览
- 人性化
- 面向程序员
- 表单
- 基础
- 概览
- 表单API
- 内建的Widget
- 高级
- 整合媒体
- 开发过程
- 设置
- 概览
- 应用程序
- 异常
- 概览
- django-admin 和 manage.py
- 添加自定义的命令
- 测试
- 介绍
- 部署
- 概述
- WSGI服务器
- 部署静态文件
- 通过email追踪代码错误
- Admin
- 管理操作
- 管理文档生成器
- 安全
- 安全概述
- 说明Django中的安全问题
- 点击劫持保护
- 加密签名
- 国际化和本地化
- 概述
- 本地化WEB UI格式化输入
- “本地特色”
- 常见的网站应用工具
- 认证
- 概览
- 使用认证系统
- 密码管理
- 日志
- 分页
- 会话
- 数据验证
- 其它核心功能
- 按需内容处理
- 重定向
- 信号
- 系统检查框架