日志

    Python 的logging 配置由四个部分组成:

    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 将ERRORCRITICAL 消息发送给一个页面服务,而用另外一个hander 将所有的消息(包括 ERRORCRITICAL 消息)记录到一个文件中用于以后进行分析。

    Filters

    Filter 用于对从logger 传递给handler 的日志记录进行额外的控制。

    默认情况下,满足日志级别的任何消息都将被处理。通过安装一个filter,你可以对日志处理添加额外的条件。例如,你可以安装一个filter,只允许处理来自特定源的ERROR 消息。

    Filters 还可以用于修改将要处理的日志记录的优先级。例如,如果日志记录满足特定的条件,你可以编写一个filter 将日志记录从ERROR 降为WARNING

    Filters 可以安装在logger 上或者handler 上;多个filter 可以串联起来实现多层filter 行为。

    Formatters

    最后,日志记录需要转换成文本。Formatter 表示文本的格式。Fomatter 通常由包含日志记录属性的Python 格式字符串组成;你也可以编写自定义的fomatter 来实现自己的格式。

    配置好logger、handler、filter 和formatter 之后,你需要在代码中放入logging 调用。使用logging 框架非常简单。下面是个例子:

    就是这样!每次满足bad_mojo 条件,将写入一条错误日志记录。

    命名logger

    logging.getLogger() 调用获取(如有必要则创建)一个logger 的实例。Logger 实例通过名字标识。Logger 使用名称的目的是用于标识其配置。

    Logger 的名称习惯上通常使用__name__,即包含该logger 的Python 模块的名字。这允许你基于模块filter 和handle 日志调用。如果你想使用其它方式组织日志消息,可以提供点号分隔的名称来标识你的logger:

    1. # Get an instance of a specific named logger
    2. 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.interestingproject.interesting.stuff logger 上的所有日志消息。

    这种传播行为可以基于每个logger 进行控制。如果你不想让某个logger 传播消息给它的上一级,你可以关闭这个行为。

    logging 调用

    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 使用dictConfig 格式

    为了配置logging,你需要使用 来定义字典形式的logging 设置。这些设置描述你的logging 设置的logger、handler、filter 和formatter,以及它们的日志等级和其它属性。

    默认情况下,LOGGING 设置与进行合并。

    如果LOGGING 中的disable_existing_loggers 键为True(默认值),那么默认配置中的所有logger 都将禁用。Logger 的禁用与删除不同;logger 仍然存在,但是将默默丢弃任何传递给它的信息,也不会传播给上一级logger。所以,你应该非常小心使用'disable_existing_loggers': True;它可能不是你想要的。你可以设置disable_existing_loggersFalse,并重新定义部分或所有的默认loggers;或者你可以设置 为 None,并 自己处理logging 配置

    Logging 的配置属于Django setup() 函数的一部分。所以,你可以肯定在你的项目代码中logger 是永远可用的。

    的完整文档是logging 字典配置最好的信息源。但是为了让你尝尝,下面是几个例子。

    1. LOGGING = {
    2. 'version': 1,
    3. 'disable_existing_loggers': False,
    4. 'handlers': {
    5. 'file': {
    6. 'level': 'DEBUG',
    7. 'class': 'logging.FileHandler',
    8. },
    9. },
    10. 'loggers': {
    11. 'django.request': {
    12. 'handlers': ['file'],
    13. 'level': 'DEBUG',
    14. 'propagate': True,
    15. },
    16. },
    17. }

    如果你使用这个示例,请确保修改'filename' 路径为运行Django 应用的用户有权限写入的一个位置。

    其次,下面这个示例演示如何让日志系统将Django 的日志打印到控制台。django.requestdjango.security 不会传播日志给上一级。它在本地开发期间可能有用。

    默认情况下,这个配置只会将INFO 和更高级别的日志发送到控制台。Django 中这样的日志信息不多。可以设置环境变量DJANGO_LOG_LEVEL=DEBUG 来看看Django 的debug 日志,它包含所有的数据库查询所以非常详尽。

    1. import os
    2. LOGGING = {
    3. 'version': 1,
    4. 'disable_existing_loggers': False,
    5. 'handlers': {
    6. 'console': {
    7. 'class': 'logging.StreamHandler',
    8. },
    9. },
    10. 'loggers': {
    11. 'django': {
    12. 'handlers': ['console'],
    13. 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
    14. },
    15. }

    最后,下面是相当复杂的一个logging 设置:

    这个logging 配置完成以下事情:

    • 以‘dictConfig version 1’格式解析配置。目前为止,这是dictConfig 格式唯一的版本。

    • 定义两个formatter:

      • simple,它只输出日志的级别(例如,DEBUG)和日志消息。

        format 字符串是一个普通的Python 格式化字符串,描述每行日志的细节。输出的完整细节可以在formatter 文档中找到。

      • 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 —— consolemail_admins。这表示所有INFO(和更高级)的消息将打印到控制台上;ERRORCRITICAL 消息还会通过邮件发送出来。

    自定义logging 配置

    如果你不想使用Python 的dictConfig 格式配置logger,你可以指定你自己的配置模式。

    LOGGING_CONFIG 设置定义一个可调用对象,将它用来配置Django 的logger。默认情况下,它指向Python 的 函数。但是,如果你想使用不同的配置过程,你可以使用其它只接受一个参数的可调用对象。配置logging 时,将使用LOGGING 的内容作为参数的值。

    禁用logging 配置

    如果你完全不想配置logging(或者你想使用自己的方法手工配置logging),你可以设置LOGGING_CONFIGNone。这将禁用 的配置过程。下面的示例禁用Django 的logging 配置,然后手工配置logging:

    settings.py

    1. LOGGING_CONFIG = None
    2. import logging.config
    3. logging.config.dictConfig(...)

    设置LOGGING_CONFIGNone 只表示禁用自动配置过程,而不是禁用logging 本身。如果你禁用配置过程,Django 仍然执行logging 调用,只是调用的是默认定义的logging 行为。

    Django 提供许多工具用于处理在网站服务器环境中独特的日志需求。

    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 语句。

    由于性能原因,SQL的日志只在设置之后开启。DEBUG 设置为True,无论日志级别或者安装的处理器是什么。

    这里的日志不包含框架级别的的初始化(例如,SET TIMEZONE)和事务管理查询(例如,BEGINCOMMITROLLBACK)。如果你希望看到所有的数据库查询,可以打开数据库中的查询日志。

    django.security.*

    Security logger 将收到任何出现 的消息。SuspiciousOperation 的每个子类型都有一个子logger。日志的级别取决于异常处理的位置。大部分情况是一个warning 日志,而如果SuspiciousOperation 到达WSGI handler 则记录为一个error。例如,如果请求中包含的HTTP Host 头部与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:

    1. 'loggers': {
    2. 'django.security.DisallowedHost': {
    3. 'handlers': ['null'],
    4. 'propagate': False,
    5. },
    6. },

    django.db.backends.schema

    迁移框架执行的SQL 查询会改变数据库的模式时,则记录这些SQL 查询。注意,它不会记录 执行的查询。

    Handlers

    在Python logging 模块提供的handler 基础之上,Django 还提供另外一个handler。

    class (include_html=False, email_backend=None)

    这个handler 将它收到的每个日志信息用邮件发送给站点管理员。

    如果日志记录包含request 属性,该请求的完整细节都将包含在邮件中。

    如果日志记录包含栈回溯信息,该栈回溯也将包含在邮件中。

    AdminEmailHandlerinclude_html 参数用于控制邮件中是否包含HTML 附件,这个附件包含DEBUGTrue 时的完整网页。若要在配置中设置这个值,可以将它包含在django.utils.log.AdminEmailHandler handler 的定义中,像下面这样:

    1. 'handlers': {
    2. 'mail_admins': {
    3. 'level': 'ERROR',
    4. 'class': 'django.utils.log.AdminEmailHandler',
    5. 'include_html': True,
    6. }
    7. },

    注意,邮件中的HTML 包含完整的回溯栈,包括栈每个层级局部变量的名称和值以及你的Django 设置。这些信息可能非常敏感,你也许不想通过邮件发送它们。此时可以考虑使用类似 这样的东西,回溯栈的完整信息和安全信息不会 通过邮件发送。你还可以从错误报告中显式过滤掉特定的敏感信息 —— 更多信息参见过滤错误报告

    通过设置AdminEmailHandleremail_backend 参数,可以覆盖handler 使用的,像这样:

    默认情况下,将使用EMAIL_BACKEND 中指定的邮件后端。

    send_mail(subject, message, *args, **kwargs)

    New in Django 1.8.

    发送邮件给管理员用户。若要自定它的行为,可以子类化AdminEmailHandler 类并覆盖这个方法。

    Filters

    在Python logging 模块提供的过滤器的基础之上,Django 还提供两个过滤器。

    class CallbackFilter(callback)[source]

    这个过滤器接受一个回调函数(它接受一个单一参数,也就是要记录的东西),并且对每个传递给过滤器的记录调用它。如果回调函数返回False,将不会进行记录的处理。

    例如,要从admin邮件中过滤掉(只在用户取消上传时产生),你可以创建一个过滤器函数:

    1. from django.http import UnreadablePostError
    2. def skip_unreadable_post(record):
    3. if record.exc_info:
    4. exc_type, exc_value = record.exc_info[:2]
    5. if isinstance(exc_value, UnreadablePostError):
    6. return False
    7. return True

    然后把它添加到logger的配置中:

    1. 'filters': {
    2. 'skip_unreadable_posts': {
    3. '()': 'django.utils.log.CallbackFilter',
    4. 'callback': skip_unreadable_post,
    5. }
    6. },
    7. 'handlers': {
    8. 'mail_admins': {
    9. 'level': 'ERROR',
    10. 'filters': ['skip_unreadable_posts'],
    11. 'class': 'django.utils.log.AdminEmailHandler'
    12. }
    13. },

    class RequireDebugFalse[source]

    这个过滤器只在设置后传递记录。DEBUG 为 False。

    这个过滤器遵循 默认的配置,以确保AdminEmailHandler只在为False的时候发送错误邮件。

    1. 'filters': {
    2. 'require_debug_false': {
    3. '()': 'django.utils.log.RequireDebugFalse',
    4. }
    5. },
    6. 'handlers': {
    7. 'mail_admins': {
    8. 'level': 'ERROR',
    9. 'filters': ['require_debug_false'],
    10. 'class': 'django.utils.log.AdminEmailHandler'
    11. }
    12. },

    class RequireDebugTrue[source]

    这个过滤器类似于,除了记录只在DEBUGTrue时传递的情况。

    默认情况下,Django 的logging 配置如下:

    当 为True 时:

    • django的全局logger会向控制台发送级别等于或高级INFO的所有消息。Django在这个时候并不会做任何日志调用(所有在DEBUG级别上的日志,或者被django.requestdjango.security处理的日志)。
    • py.warnings logger,它处理来自warnings.warn()的消息,会向控制台发送消息。

    DEBUGFalse 时:

    另见来了解如何补充或者替换默认的日志配置。