Django中的logging
對網站、微服務來說,log(日誌)是比較重要的運維工具。ofollow,noindex" target="_blank">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策略。