Django中的logging
2018-11-17 09:30:39 +08 字数:2656 标签: Django Python对网站、微服务来说,log(日志)是比较重要的运维工具。
Django的log,主要是复用Python标准库中的logging模块,在settings.py
中进行配置。
此外,也提供了一些独特的扩展。
完整示例 ¶
以下为settings.py
的片段:
TIME_ZONE = 'Asia/Shanghai'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{asctime} {module}.{funcName} {lineno:3} {levelname:7} => {message}',
'style': '{',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'verbose',
'filename': '/tmp/django.log',
'maxBytes': 4194304, # 4 MB
'backupCount': 10,
'level': 'DEBUG',
},
},
'loggers': {
'': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
'django': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
'propagate': False,
},
},
}
设置时间与时区 ¶
在log中,通过在format
设置{asctime}
,可以看到时间。
时间很可能是log最重要的一个属性。
而对中国用户来说,默认时区是个必须关注的问题,否则时间和当地时间差距较大。
需要在settings.py
中,做TIME_ZONE
配置。
TIME_ZONE = 'Asia/Shanghai'
有这个配置在,可以不用修改系统的配置。
formatters ¶
'formatters': {
'verbose': {
'format': '{asctime} {module}.{funcName} {lineno:3} {levelname:7} => {message}',
'style': '{',
},
},
format ¶
Formatter就是log格式化的样式。
这里只定义了一种formatter,那就是verbose
。
{asctime}
:默认的日期与时间,形式大概是2018-11-15 20:41:56,868
。这个形式可以修改,通过datefmt
。{module}
是模块名。{funcName}
是函数名。{lineno:3}
是行号,至少显示3个字符,少则补空格。{levelname:7}
是log级别,INFO
、ERROR
这些。{message}
是log内容。
其它可选字段,详见logrecord。
style ¶
这里style
选择{
,是指{asctime}
这种形式。
如果选择%
,则是%(asctime)s
这种形式。
还有一种选择,是$
,是$asctime
或${asctime}
这种形式。
详见Use of alternative formatting styles。
datefmt ¶
Django自带模块默认的格式化形式,打印出来,日期时间的形式大致是这样:16/Nov/2018 09:03:22
。
相当于设置了下面的datefmt
。
'formatters': {
'default': {
...
'style': '{',
'datefmt': '%d/%b/%Y %H:%M:%S',
},
},
其中,各个组成部分的含义与其它可选内容见下表:
Directive | Meaning Notes |
---|---|
%a | Locale’s abbreviated weekday name. |
%A | Locale’s full weekday name. |
%b | Locale’s abbreviated month name. |
%B | Locale’s full month name. |
%c | Locale’s appropriate date and time representation. |
%d | Day of the month as a decimal number [01,31]. |
%H | Hour (24-hour clock) as a decimal number [00,23]. |
%I | Hour (12-hour clock) as a decimal number [01,12]. |
%j | Day of the year as a decimal number [001,366]. |
%m | Month as a decimal number [01,12]. |
%M | Minute as a decimal number [00,59]. |
%p | Locale’s equivalent of either AM or PM. (1) |
%S | Second as a decimal number [00,61]. (2) |
%U | Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3) |
%w | Weekday as a decimal number [0(Sunday),6]. |
%W | Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3) |
%x | Locale’s appropriate date representation. |
%X | Locale’s appropriate time representation. |
%y | Year without century as a decimal number [00,99]. |
%Y | Year with century as a decimal number. |
%z | Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59]. |
%Z | Time zone name (no characters if no time zone exists). |
%% | A literal ‘%’ character. |
logging默认格式中,asctime
的逗号,
后面是三位毫秒(milliseconds),在以上格式中并不存在。
因此,如果修改为自定义格式,毫秒信息丢失。
如果要额外添加,需要指定{msecs:03d}
。
所以,如果需要毫秒信息,一般不改默认格式。
handlers ¶
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'verbose',
'filename': '/tmp/django.log',
'maxBytes': 4194304, # 4 MB
'backupCount': 10,
'level': 'DEBUG',
},
},
formatter ¶
formatter
是指定一个格式化方式,也就是前面formatters
定义的那些。
这里选择了前面定义的verboase
。
level ¶
level
是选择log的最低等级。
由低到高,列出如下,需要按需选择。
- DEBUG: Low level system information for debugging purposes.
- INFO: General system information.
- WARNING: Information describing a minor problem that has occurred.
- ERROR: Information describing a major problem that has occurred.
- CRITICAL: Information describing a critical problem that has occurred.
class ¶
class
是指定处理log的类,在Python里logging.handlers中。
可选class
列出如下,详见Useful Handlers。
class | 功能 |
---|---|
StreamHandler | 输出到Stream。通常用来打印到标准输出。 |
FileHandler | 打印到文件。 |
NullHandler | 不格式化也不打印。主要是为了避免No handlers could be found for logger XXX 的设计。 |
WatchedFileHandler | 自动重开log文件,配合别的会自动切分的log文件使用。 |
RotatingFileHandler | 自动按大小切分的log文件。 |
TimedRotatingFileHandler | 按时间自动切分的log文件。 |
SocketHandler | 向Socket打log,基于TCP协议。 |
DatagramHandler | 向Socket打log,基于UDP协议。 |
SysLogHandler | 在Unix-like系统打印到remote或local的Unix syslog。 |
NTEventLogHandler | 在Windows系统打印到微软的event log。 |
SMTPHandler | 通过email发送log。 |
MemoryHandler | 打印到内存buffer。 |
HTTPHandler | 通过HTTP协议向服务器发送log。 |
QueueHandler | 打log到Queue中,适合多进程(multiprocessing)场景。 |
这里主要选择了StreamHandler和RotatingFileHandler。
既能在./manage.py runserver
时打印在前台,又能在部署后打印到文件,方便前期开发和后期运维。
此外,Django额外定义了一个handler——AdminEmailHandler,向管理员发送邮件。
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
'include_html': True,
}
},
有需要可以添加,但要确保邮件SMTP服务器的通畅。
loggers ¶
'loggers': {
'': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
'django': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
'propagate': False,
},
},
到了loggers
模块,最重要的是选择设置哪些logger。
这里设置了''
(root)和'django'
。
前者是为了方便地使用logging
,所以设了root的logger,这也是默认的logger。
后者是打印所有Django自身的logger。
- django
- django.request
- django.server
- django.template
- django.db.backends
- django.security.*
- django.security.DisallowedHost
- django.security.SuspiciousOperation
- django.security.SuspiciousOperation
- django.security.DisallowedHost
- django.db.backends.schema
propagate
是设定是否向父logger传播信息。
对root来说,这一条可有可无。
但在本例中,root被使用了,所以'django'
logger必须设置为'propagate': False,
,否则会打印两次。
其它 ¶
现在dictConfig
只有一个version
,那就是1
。
disable_existing_loggers
默认是True
,禁用已经存在的logger。
被禁用的logger仍然存在,只是不接收任何输入,包括其父logger也得不到信息
——这种行为可能导致一些问题,因此这一条通常固定设为False
。
Django还支持一些Filters,在打log到指定目标时预先过滤一些内容。 这里不使用,也未介绍。
代码示例 ¶
import logging as log
log.debug('Hello %s', 'world')
log.info('I am here!')
配置好一切后,使用起来就是这么简单。
当然,这里是因为使用了root这个logger,才不需要使用loggging.getLogger()
。
如果是写提供给别人使用的库或Django App,需要考虑更有层次的log策略。