Django的日志记录
logging快速入门
Django使用了Python内置模块logging
作为系统日志记录。其设置包括4个部分:Loggers、Handlers、Filters和Formatters。
Loggers记录器
logger记录器是logging系统的入口,可以将消息写入到之中进行处理。logger可以设置日志的级别。此日志级别描述了记录器将处理的消息的严重性。 Python定义了以下日志级别:
级别 | 数值 | 意义 |
---|---|---|
NOTSET |
0 | 所有消息 |
DEBUG |
10 | 用于调试目的的低级系统信息 |
INFO |
20 | 一般系统信息 |
WARNING |
30 | 描述已发生的小问题的 |
ERROR |
40 | 描述已发生的主要问题 |
CRITICAL |
50 | 描述已发生的严重问题的信息 |
写入logger的每条消息都是日志记录。每个日志记录还具有一个日志级别,指示该特定消息的严重性。日志记录还可以包含描述正在记录的事件的有用元数据。这可以包括诸如堆栈跟踪或错误代码之类的详细信息。
当消息被发送给logger时,会将消息的日志级别与logger的日志级别进行比较。如果邮件的日志级别达到或超过记录器本身的日志级别,则消息将进行进一步处理。如果没有,则将忽略该消息。
一旦记录器确定需要处理消息,它就会传递给处理程序。
Handler处理程序
日志处理程序是确定logger每一条消息如何处理的引擎。它描述了特定的日志记录行为,例如将消息写入屏幕、文件或网络套接字。
与logger一样,处理程序也具有日志级别。 如果日志记录的日志级别未达到或超过处理程序的级别,则处理程序将忽略该消息。
记录器可以有多个
处理程序,每个处理程序可以具有不同
的日志级别。通过这种方式,可以根据消息的重要性提供不同形式的通知。例如,可以安装一个将ERROR和CRITICAL消息转发给分页服务的处理程序,而第二个处理程序将所有消息(包括ERROR和CRITICAL消息)记录到文件中以供以后分析。
Filters过滤器
使用过滤器可以在从记录器到日志处理程序过程对消息进行额外的控制。
默认情况下,将处理满足日志级别要求的任何日志消息。但是,通过安装筛选器,可以在日志记录过程中添加其他条件。 例如,可以安装仅允许发出来自特定源的ERROR消息的过滤器。
过滤器还可用于修改日志记录级别。例如,如果满足一组特定条件,可以编写一个过滤器,将ERROR日志记录降级为WARNING记录。
过滤器可以安装在记录器或处理器上; 可以在链中使用多个过滤器来执行多个过滤操作。
Formatter日志格式
最终,日志记录需要呈现为文本。格式化程序描述该文本的确切格式。格式化程序通常由包含LogRecord
属性的Python格式化字符串组成; 但是,也可以编写自定义格式化程序来实现特定的格式化行为。
使用日志记录
在视图函数中使用日志:
# 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!')
在使用getLogger
创建记录器时,默认的记录级别是WARNING
(logging也是记录警告级别以上的消息)
命名记录器
使用logging.getlogger
来创建一个记录器实例。按照惯例,记录器名称通常是__name__
,即包含记录器的Python模块的名称。 这允许您基于每个模块对日志记录进行过滤。 但是,如果有其他组织日志消息的方法,则可以提供任何以.
分隔的名称来标识记录器:
# Get an instance of a specific named logger
logger = logging.getLogger('project.interesting.stuff')
使用.
分隔的记录器命名定义了记录的层次结构。 project.interesting
logger是project.interesting.stuff
logger的父级; project
logger是project.interesting
logger的父级。
通过设置层次结构, 记录器可以设置为将他们的日志记录调用传递给他们的父级记录器。通过这种方式,可以在记录器树的根目录
中定义一组处理程序,并捕获子记录器的所有日志记录调用。在项目命名空间中定义的日志处理程序会捕获在project.interesting和project.interesting.stuff记录器上发出的所有日志消息。
可以在每个记录器都设置是否进行记录传递,如果您不希望特定记录器传播到其父项,则可以关闭此行为:logger.propagate = False
调用日志记录
对于上面提到的每个级别的记录,logger实例都提供对应的调用方法:
- logger.debug()
- logger.info()
- logger.warning()
- logger.error()
- logger.critical()
除此之外,还有几个调用方法:
logger.log()
: 记录消息并手动指定消息的日志级别;logger.exception()
: 记录一个级别为ERROR的日志,只能被异常处理程序调用;logger.setLevel()
:设置记录的消息门槛,低于设置的级别的消息会被忽略;
设置日志
要在Django中使用日志记录,只在代码中调用logging函数是不够的。还需要配置记录器,处理程序,过滤器和格式化程序,以确保以有用的方式输出日志记录输出。Python的日志库提供了几种配置日志记录的技术,从编程接口到配置文件。默认情况下,Django使用dictConfig
格式。
要配置日志记录,需要在settings中设置LOGGING
字典表。内中设置描述了日志记录设置中所需的记录器,处理程序,过滤器和格式化程序,以及自定义的这些组件具有的日志级别和其他属性。如下,它设置将来自django记录器的所有日志记录写入本地文件
,要确保有权限:
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,
},
},
}
当disable_existing_loggers
设置为True(默认值)时,则禁用默认配置中的所有
记录器。已禁用的记录器与已删除的记录器不同;记录器仍然存在,但会默默地丢弃记录到它的任何内容,甚至不会将记录传递到父记录器。因此,设置'disable_existing_loggers' : True
时要非常小心,它可能导致意料之外的结果。相反,可以将disable_existing_loggers
设置为False并重新定义部分或全部默认记录器;或者可以将LOGGING_CONFIG
设置为None
并自己处理日志记录配置:
settings.py¶
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
下面的例子介绍如何打印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'),
},
},
}
运行项目:
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
(0.000)
SELECT name, type FROM sqlite_master
WHERE type in ('table', 'view') AND NOT name='sqlite_sequence'
ORDER BY name; args=None
(0.000) SELECT "django_migrations"."app", "django_migrations"."name" FROM "django_migrations"; args=()
November 23, 2018 - 17:22:20
Django version 2.1.2, using settings 'django_ulysses.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
(0.001) SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" WHERE "auth_user"."id" = 1; args=(1,)
可以看到非常详细的内容。
以下是完整的LOGGING
设置:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'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']
}
}
}
version
: 根据Python的logging模块,1
是目前唯一
的有效值;formatters
:simple
模式,只输出日志级别名称(例如,DEBUG)和日志消息;verbose
,输出日志级别名称,日志消息,以及生成日志消息的时间,进程,线程和模块。format是普通的python格式字符串,详细内容在logging.Formatter
filters
:special
是自定义过滤器project.logging.SpecialFilte
的别名,如果此过滤器需要其他参数,则可以将它们作为过滤器配置字典中的附加键提供。 在这种情况下,在实例化SpecialFilter
时,参数foo将被赋予bar值;django.utils.log.RequireDebugTrue
:当DEBUG=True时,进行记录的传递;
handlers
:日志处理程序console
:使用StreamHandler
,会将INFO级以上的记录打印到控制台,输出格式选择simple
;mail_admins
: 会将EEOR及以上级别的记录通过邮件发送给站点的ADMINS
(ADMINS = [(‘John’, ‘[email protected]’), (‘Mary’, ‘[email protected]’)]),使用special
过滤器;
logger
记录器:django
:会将所有消息全部传递到console
处理程序;django.request
: 它将所有ERROR消息传递给mail_admins
处理程序。 此外,此记录器标记为不传递
消息。 这意味着django
记录器不会处理写入django.request
的日志消息。myproject.custom
:自定义记录器,它传递INFO或更高级别的所有消息,这些消息经过special
过滤器传递给两个处理程序 -console
和mail_admins
。 这意味着所有INFO级别消息(或更高级别)将打印到控制台; ERROR和CRITICAL消息也将通过电子邮件输出。
Django 日志扩展
Django的内置logger记录器
django
:django记录器层次结构中最底层的,不会使用它记录消息;django.request
:记录与请求
处理相关的消息。 5XX响应被引发为ERROR
消息; 4XX响应被引发为WARNING
消息。 记录到django.security
记录器的请求不会记录到django.request。发送给此记录器的消息具有以下额外上下文:- status_code:与请求关联的HTTP响应代码。
- request:生成日志消息的请求对象。
django.server
:记录与runserver
命令调用的服务器接收的请求的处理相关的消息。 HTTP 5XX响应记录为ERROR消息,4XX响应记录为WARNING消息,其他所有响应记录为INFO。同样需要status_code和requestdebug.template
: 记录与模板呈现相关的消息;django.db.backends
:处理程序和数据库交互相关的消息。例如,请求执行的每个应用程序级SQL语句都会在DEBUG级别记录到此记录器。发送给此记录器的消息具有以下额外上下文:duration:执行SQL语句所需的时间,sql:已执行的SQL语句,params:SQL调用中使用的参数。出于性能原因,只有在settings.DEBUG
设置为True
时才会启用SQL日志记录,而不管安装的日志记录级别或处理程序如何。此日志记录不包括框架级初始化(例如SET TIMEZONE)或事务管理查询(例如BEGIN,COMMIT和ROLLBACK)。 如果要查看所有数据库查询,请在数据库中启用查询日志记录。django.security.*
:安全记录器将在发生任何SuspiciousOperation和其他与安全相关的错误时收到消息。每个安全错误子类型都有一个子记录器,包括所有SuspiciousOperations。日志事件的级别取决于处理异常的位置。大多数事件都记录为警告
,而到达WSGI处理程序的任何SuspiciousOperation都将记录为错误
。例如,当HTTP Host Host包含在来自客户端的请求中,该请求与ALLOWED_HOSTS不匹配时,Django将返回400响应,并将向django.security.DisallowedHost记录器记录一条错误消息。默认情况下,这些日志事件将到达django记录器,当DEBUG = False
时,会将错误事件邮寄给管理员
。由于SuspiciousOperation导致400响应的请求将不会记录到django.request记录器,而只记录到django.security记录器。django.db.backends.schema
:记录通过迁移框架更改数据库时执行的sql查询。它不会记录RunPython执行的查询。 发送给此记录器的消息在其额外的上下文中有params和sql(但与django.db.backends不同,不是duration);
处理程序
Django提供了一个处理日志的处理程序:AdminEmailHandler
,它用来记录消息并发送给站点的admin。
过滤器
Django提供几个基于python 日志模块的过滤器:
class CallbackFilter(callback)
:此过滤器接受回调函数(应接受单个参数,要记录的记录),并为通过过滤器的每个记录调用它。 如果回调返回False,则不会继续处理该记录。例如,要从管理员电子邮件中过滤掉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
然后在logging设置中添加这个回调函数:
'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
: 只有当settings中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
: 只有当settings中DEBUG = True
时,才会传递记录,使用方法类似上面的。
Django默认的logging设置
DEBUG
为True
:django记录器将INFO级别或更高级别的消息发送给django
记录器层(除了django.server
)打印到控制台;
DEBUG
为False
:会将ERROR和CRITICAL级别的消息发送给AdminEmailHandler.
django.server
会将INFO级别及以上的消息打印到控制台,与DEBUG
的设置无关。