日志¶ T0>

日志快速入门

Django使用Python内建的logging模块打印系统日志。 该模块的用法在Python本身的文档中有详细的讨论。 如果你从来没有使用过Python的logging框架(或者即使使用过),请参见下面的快速入门。

logging的组成

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

Loggers

Logger为日志系统的入口。 每个logger是一个带有名称的bucket,你可以向这个bucket写入需要处理的消息。

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

Filters

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

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

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

Filters可以安装在loggers或handlers上;可以将多个filters链接起来执行多个过滤操作。

Formatters

最后,日志记录需要转换成文本。 Formatter描述文本的准确格式。 Formatter通常由包含日志记录的属性的Python格式化字符串组成;但是,你也可以编写自定义formatters来实现特定的格式化行为。

使用logging

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

# 导入logging库
import logging

# 获取logger的一个实例
logger = logging.getLogger(__name__)

def my_view(request, arg1, arg):
    ...
    if bad_mojo:
        # 记录一条错误信息
        logger.error('Something went wrong!')

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

命名loggers

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

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

# 获取一个特定名称的logger
logger = logging.getLogger('project.interesting.stuff')

点号分隔的logger名称定义一个层级。 project.interesting logger被认为是project.interesting.stuff logger 的父级;project logger是project.interestinglogger的父级。

层级为何如此重要? 因为可以设置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

当然,只是将logging 调用放入你的代码中还是不够的。 你还需要配置logger、handler、filter 和formatter 来确保日志的输出是有意义的。

Python的logging库提供几种配置logging的技术,从程序接口到配置文件。 默认情况下,Django使用dictConfig格式

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

默认情况下,LOGGING设置与Django默认的logging配置使用下面的模式进行合并。

如果LOGGING中的disable_existing_loggers键为True(默认值),那么默认配置中的所有logger都将禁用。 禁用loggers与删除不一样;loggers仍然存在,但会静默地丢弃任何记录到它的记录,甚至不会将条目传播到父logger。 所以你应该非常小心使用 'disable_existing_loggers': True;这可能不是你想要的。 相反,你可以将disable_existing_loggers设置为False,并重新定义部分或全部默认loggers;或者你可以将LOGGING_CONFIG设置为None自己处理logging配置

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

示例

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

首先,这是一个简单的配置,将所有记录从django logger写入本地文件:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/path/to/django/debug.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

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

其次,下面这个示例演示如何让日志系统将Django 的日志打印到控制台。 它在本地开发期间可能有用。

默认情况下,此配置只将级别INFO或更高版本的消息发送到控制台(与Django的默认日志记录配置相同),但默认情况下仅在DEBUG=True时显示日志记录)。 Django 中这样的日志信息不多。 然而,使用此配置,你还可以设置环境变量DJANGO_LOG_LEVEL=DEBUG,以查看Django的所有调试日志记录,这非常详细,因为它包括所有数据库查询:

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',
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'filters': ['special']
        }
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'propagate': True,
        },
        '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 Objects中找到可以输出的详细列表。

    • verbose,它输出日志级别、日志消息,以及时间、进程、线程和生成日志消息的模块。

  • 定义两个过滤器:

    • project.logging.SpecialFilter,使用别名special 如果此过滤器需要其他参数,则可以在过滤器配置字典中将其作为附加键提供。 在这种情况下,实例化SpecialFilter时,参数foo将被赋值为bar
    • django.utils.log.RequireDebugTrue, which passes on records when DEBUG is True.
  • 定义两个处理程序:

    • console,一个StreamHandler,它将向stderr打印任何INFO(或更高版本)的消息。 这个handler 使用simple 输出格式。
    • mail_admins,一个AdminEmailHandler,它将用邮件发送ERROR(和更高级)的消息到站点管理员。 这个handler 使用special filter。
  • 配置三个logger:

    • django,它将所有消息传递到console处理程序。
    • django.request,它将把所有的ERROR日志信息发送给mail_admins handler。 另外,标记这个logger 向它的父级传递日志消息(即propagate的值为False)。 这表示写入django.request 的日志信息将不会被django logger 处理。
    • myproject.custom,将把INFO级别(更高级别)的日志经过special过滤器过滤后,发送给consolemail_admins这俩个handler。 这意味着所有INFO级消息(或更高级别)将被打印到控制台; ERRORCRITICAL消息也将通过电子邮件输出。

自定义logging配置

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

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

禁用logging配置

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

settings.py
LOGGING_CONFIG = None

import logging.config
logging.config.dictConfig(...)

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

Django logging的扩展

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

Loggers

Django 提供几个内建的logger。

django

django层次结构中捕获全部消息的logger。 没有使用此名称发布的消息,而是使用下面的logger之一。

django.request

记录与处理请求相关的消息。 5XX响应被提出为ERROR消息; 4XX响应提升为WARNING消息。

这个logger 的消息具有以下额外的上下文:

  • status_code:请求的HTTP 响应码。
  • request:生成日志信息的请求对象。

django.server

Django中的新功能1.10。

记录与由runserver命令调用的服务器接收到的请求的处理相关的消息。 HTTP 5XX响应记录为ERROR消息,4XX响应记录为WARNING消息,其他所有内容都记录为INFO

这个logger 的消息具有以下额外的上下文:

  • status_code:请求的HTTP 响应码。
  • request:生成日志信息的请求对象。

django.template

记录与渲染模板相关的消息。

  • 缺少上下文变量记录为DEBUG消息。
  • Uncaught exceptions raised during the rendering of an {% include %} are logged as WARNING messages when debug mode is off (helpful since {% include %} silences the exception and returns an empty string in that case).

django.db.backends

与数据库交互的代码相关的消息。 例如,HTTP请求执行应用级别的SQL 语句将以DEBUG 级别记录到该logger。

这个logger 的消息具有以下额外的上下文:

  • duration:执行SQL 语句花费的时间。
  • sql:执行的SQL 语句。
  • params:SQL 调用中用到的参数。

出于性能原因,仅当settings.DEBUG设置为True时才启用SQL日志记录,无论安装的日志记录级别或处理程序如何。

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

django.security.*

任何出现SuspiciousOperation和其他与安全相关的错误,安全记录器将收到消息。 每个安全错误子类型都有一个子记录器,包括所有SuspiciousOperation。日志事件的级别取决于处理异常的位置。 大部分情况是一个warning 日志,而如果SuspiciousOperation 到达WSGI handler 则记录为一个error。 例如,如果请求中包含的HTTP Host 头部与ALLOWED_HOSTS 不匹配,Django 将返回400 响应,同时将记录一个error 消息到django.security.DisallowedHost logger。

默认情况下,这些日志事件将到达django记录器,当DEBUG=False时,会将错误事件发送给管理员。 由于django.security 导致400 响应的请求不会在SuspiciousOperation logger 中记录日志,而只在django.request logger 中记录日志。

要使某个特定类型的SuspiciousOperation静默,您可以按照以下示例覆盖该特定的记录器:

'handlers': {
    'null': {
        'class': 'logging.NullHandler',
    },
},
'loggers': {
    'django.security.DisallowedHost': {
        'handlers': ['null'],
        'propagate': False,
    },
},

其他django.security记录器不是基于SuspiciousOperation是:

django.db.backends.schema

migrations framework执行的SQL 查询会改变数据库的模式时,则记录这些SQL 查询。 注意,它不会记录RunPython 执行的查询。 对这个记录器的消息在其额外的上下文中具有paramssql(但不同于django.db.backends,而不是持续时间)。 这些值与django.db.backends中说明的含义相同。

Django中的新功能1.10:

添加了extra上下文。

Handlers

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

class AdminEmailHandler(include_html=False, email_backend=None)[source]

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

如果日志记录包含request 属性,该请求的完整细节都将包含在邮件中。 如果客户端的IP地址在INTERNAL_IPS设置中,电子邮件主题将包含短语“内部IP”;如果不是,它将包括“EXTERNAL IP”。

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

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

'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
    }
},

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

通过设置AdminEmailHandler中的handler中的email_backend参数,可以覆盖handler 使用的email backend,像这样:

'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler',
        'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
    }
},

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

send_mail(subject, message, *args, **kwargs)[source]

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

Filters

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

class CallbackFilter(callback)[source]

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

例如,要从admin邮件中过滤掉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]

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

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

'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]

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

Django的默认logging配置

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

DEBUGTrue 时:

  • django logger在INFO或更高级别以django层(django.server除外)发送消息到控制台。

DEBUGFalse 时:

  • django logger在ERRORCRITICAL级别以django层(django.server除外)发送消息到AdminEmailHandler

独立于DEBUG的值:

  • django.server logger发送INFO或更高级别的消息到控制台。

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