日志
日志快速入門
Django 使用Python 内建的
logging
子產品列印日志。該子產品的用法在Python 本身的文檔中有詳細的讨論。如果你從來沒有使用過Python 的logging 架構(或者即使使用過),請參見下面的快速導論。
logging 的組成
Python 的logging 配置由四個部分組成:
Logger 為日志系統的入口。每個logger 是一個具名的容器,可以向它寫入需要處理的消息。
每個logger 都有一個日志級别。日志級别表示該logger 将要處理的消息的嚴重性。Python 定義以下幾種日志級别:
-
:用于調試目的的底層系統資訊DEBUG
-
:普通的系統資訊INFO
-
:表示出現一個較小的問題。WARNING
-
:表示出現一個較大的問題。ERROR
-
:表示出現一個緻命的問題。CRITICAL
寫入logger 的每條消息都是一個日志記錄。每個日志記錄也具有一個日志級别,它表示對應的消息的嚴重性。每個日志記錄還可以包含描述正在列印的事件的有用元資訊。這些元資訊可以包含很多細節,例如回溯棧或錯誤碼。
當給一條消息給logger 時,會将消息的日志級别與logger 的日志級别進行比較。如果消息的日志級别大于等于logger 的日志級别,該消息将會往下繼續處理。如果小于,該消息将被忽略。
Logger 一旦決定消息需要處理,它将傳遞該消息給一個Handler。
Handler 決定如何處理logger 中的每條消息。它表示一個特定的日志行為,例如将消息寫到螢幕上、寫到檔案中或者寫到網絡socket。
與logger 一樣,handler 也有一個日志級别。如果消息的日志級别小于handler 的級别,handler 将忽略該消息。
Logger 可以有多個handler,而每個handler 可以有不同的日志級别。利用這種方式,可以根據消息的重要性提供不同形式的處理。例如,你可以用一個handler 将
ERROR
和
CRITICAL
消息發送給一個頁面服務,而用另外一個hander 将所有的消息(包括
ERROR
CRITICAL
消息)記錄到一個檔案中用于以後進行分析。
Filter 用于對從logger 傳遞給handler 的日志記錄進行額外的控制。
預設情況下,滿足日志級别的任何消息都将被處理。通過安裝一個filter,你可以對日志處理添加額外的條件。例如,你可以安裝一個filter,隻允許處理來自特定源的
ERROR
消息。
Filters 還可以用于修改将要處理的日志記錄的優先級。例如,如果日志記錄滿足特定的條件,你可以編寫一個filter 将日志記錄從
ERROR
降為
WARNING
。
Filters 可以安裝在logger 上或者handler 上;多個filter 可以串聯起來實作多層filter 行為。
最後,日志記錄需要轉換成文本。Formatter 表示文本的格式。Fomatter 通常由包含
日志記錄屬性的Python 格式字元串組成;你也可以編寫自定義的fomatter 來實作自己的格式。
使用logging
配置好logger、handler、filter 和formatter 之後,你需要在代碼中放入logging 調用。使用logging 架構非常簡單。下面是個例子:
# 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!')
就是這樣!每次滿足
bad_mojo
條件,将寫入一條錯誤日志記錄。
命名logger
logging.getLogger()
調用擷取(如有必要則建立)一個logger 的執行個體。Logger 執行個體通過名字辨別。Logger 使用名稱的目的是用于辨別其配置。
Logger 的名稱習慣上通常使用
__name__
,即包含該logger 的Python 子產品的名字。這允許你基于子產品filter 和handle 日志調用。如果你想使用其它方式組織日志消息,可以提供點号分隔的名稱來辨別你的logger:
# Get an instance of a specific named logger
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.interesting
project.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 都将禁用。Logger 的禁用與删除不同;logger 仍然存在,但是将默默丢棄任何傳遞給它的資訊,也不會傳播給上一級logger。是以,你應該非常小心使用
'disable_existing_loggers': True
;它可能不是你想要的。你可以設定
disable_existing_loggers
為
False
,并重新定義部分或所有的預設loggers;或者你可以設定
LOGGING_CONFIG
None
,并
自己處理logging 配置Logging 的配置屬于Django
setup()
函數的一部分。是以,你可以肯定在你的項目代碼中logger 是永遠可用的。
示例
的完整文檔是logging 字典配置最好的資訊源。但是為了讓你嘗嘗,下面是幾個例子。
首先,下面是一個簡單的配置,它将來自
django.requestlogger 的所有日志請求寫入到一個本地檔案:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
'django.request': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
如果你使用這個示例,請確定修改
'filename'
路徑為運作Django 應用的使用者有權限寫入的一個位置。
其次,下面這個示例示範如何讓日志系統将Django 的日志列印到控制台。
django.request
django.security
不會傳播日志給上一級。它在本地開發期間可能有用。
預設情況下,這個配置隻會将
INFO
和更進階别的日志發送到控制台。Django 中這樣的日志資訊不多。可以設定環境變量
DJANGO_LOG_LEVEL=DEBUG
來看看Django 的debug 日志,它包含所有的資料庫查詢是以非常詳盡。
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',
}
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
'loggers': {
'django': {
'handlers': ['null'],
'propagate': True,
'level': 'INFO',
},
'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
字元串是一個普通的Python 格式化字元串,描述每行日志的細節。輸出的完整細節可以在 formatter 文檔 中找到。format
-
,它輸出日志級别、日志消息,以及時間、程序、線程和生成日志消息的子產品。verbose
-
- 定義filter ——
,并使用别名project.logging.SpecialFilter
。如果filter 在構造時要求額外的參數,可以在filter 的配置字段中用額外的鍵提供。在這個例子中,在執行個體化special
時,SpecialFilter
參數的值将使用foo
bar
- 定義三個handler:
-
,一個NullHandler,它傳遞null
(和更進階)的消息給DEBUG
/dev/null
-
,一個StreamHandler,它将列印console
(和更進階)的消息到stderr。這個handler 使用DEBUG
輸出格式。simple
-
,一個AdminEmailHandler,它将用郵件發送mail_admins
(和更進階)的消息到站點管理者。這個handler 使用ERROR
filter。special
-
- 配置三個logger:
-
,它傳遞所有django
和更進階的消息給INFO
handler。null
-
django.request
消息給ERROR
handler。另外,标記這個logger 不 向上傳播消息。這表示寫入mail_admins
的日志資訊将不會被django.request
logger 處理。django
-
myproject.custom
和更進階的消息并通過INFO
filter 的消息給兩個handler ——special
console
。這表示所有mail_admins
(和更進階)的消息将列印到控制台上;INFO
ERROR
消息還會通過郵件發送出來。CRITICAL
-
自定義logging 配置
如果你不想使用Python 的dictConfig 格式配置logger,你可以指定你自己的配置模式。
LOGGING_CONFIG
設定定義一個可調用對象,将它用來配置Django 的logger。預設情況下,它指向Python 的
logging.config.dictConfig()
函數。但是,如果你想使用不同的配置過程,你可以使用其它隻接受一個參數的可調用對象。配置logging 時,将使用
LOGGING
的内容作為參數的值。
禁用logging 配置
如果你完全不想配置logging(或者你想使用自己的方法手工配置logging),你可以設定
LOGGING_CONFIG
None
。這将禁用
Django 預設logging的配置過程。下面的示例禁用Django 的logging 配置,然後手工配置logging:
settings.py
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
設定
LOGGING_CONFIG
None
隻表示禁用自動配置過程,而不是禁用logging 本身。如果你禁用配置過程,Django 仍然執行logging 調用,隻是調用的是預設定義的logging 行為。
Django’s logging extensions
Django 提供許多工具用于處理在網站伺服器環境中獨特的日志需求。
Django 提供幾個内建的logger。
django
django
是一個捕獲所有資訊的logger。消息不會直接送出給這個logger。
記錄與處理請求相關的消息。5XX 響應作為
ERROR
消息;4XX 響應作為
WARNING
這個logger 的消息具有以下額外的上下文:
-
:請求的HTTP 響應碼。status_code
-
:生成日志資訊的請求對象。request
django.db.backends
與資料庫互動的代碼相關的消息。例如,HTTP請求執行應用級别的SQL 語句将以
DEBUG
級别記錄到該logger。
-
:執行SQL 語句花費的時間。duration
-
:執行的SQL 語句。sql
-
:SQL 調用中用到的參數。params
由于性能原因,SQL的日志隻在
設定
之後開啟。DEBUG 設定為
True
,無論日志級别或者安裝的處理器是什麼。
這裡的日志不包含架構級别的的初始化(例如,
SET TIMEZONE
)和事務管理查詢(例如,
BEGIN
、
COMMIT
ROLLBACK
)。如果你希望看到所有的資料庫查詢,可以打開資料庫中的查詢日志。
django.security.*
Security logger 将收到任何出現
SuspiciousOperation
的消息。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:
'loggers': {
'django.security.DisallowedHost': {
'handlers': ['null'],
'propagate': False,
},
},
django.db.backends.schema
New in Django 1.7.
當
遷移架構執行的SQL 查詢會改變資料庫的模式時,則記錄這些SQL 查詢。注意,它不會記錄
RunPython
執行的查詢。
在Python logging 子產品提供的handler 基礎之上,Django 還提供另外一個handler。
class
AdminEmailHandler
(include_html=False, email_backend=None)
[source]這個handler 将它收到的每個日志資訊用郵件發送給站點管理者。
如果日志記錄包含
request
屬性,該請求的完整細節都将包含在郵件中。
如果日志記錄包含棧回溯資訊,該棧回溯也将包含在郵件中。
AdminEmailHandler
的
include_html
參數用于控制郵件中是否包含HTML 附件,這個附件包含
DEBUG
True
時的完整網頁。若要在配置中設定這個值,可以将它包含在
django.utils.log.AdminEmailHandler
handler 的定義中,像下面這樣:
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
}
},
注意,郵件中的HTML 包含完整的回溯棧,包括棧每個層級局部變量的名稱和值以及你的Django 設定。這些資訊可能非常敏感,你也許不想通過郵件發送它們。此時可以考慮使用類似
Sentry這樣的東西,回溯棧的完整資訊和安全資訊不會 通過郵件發送。你還可以從錯誤報告中顯式過濾掉特定的敏感資訊 —— 更多資訊參見
過濾錯誤報告通過設定
AdminEmailHandler
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)
New in Django 1.8.
發送郵件給管理者使用者。若要自定它的行為,可以子類化
AdminEmailHandler
類并覆寫這個方法。
在Python logging 子產品提供的過濾器的基礎之上,Django 還提供兩個過濾器。
CallbackFilter
(callback)
這個過濾器接受一個回調函數(它接受一個單一參數,也就是要記錄的東西),并且對每個傳遞給過濾器的記錄調用它。如果回調函數傳回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'
}
},
RequireDebugFalse
這個過濾器隻在設定後傳遞記錄。DEBUG 為 False。
這個過濾器遵循
LOGGING
預設的配置,以確定
AdminEmailHandler
隻在
DEBUG
False
的時候發送錯誤郵件。
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
RequireDebugTrue
這個過濾器類似于
RequireDebugFalse
,除了記錄隻在
DEBUG
True
時傳遞的情況。
Django’s default logging configuration
預設情況下,Django 的logging 配置如下:
DEBUG
True
時:
-
的全局logger會向控制台發送級别等于或進階django
的所有消息。Django在這個時候并不會做任何日志調用(所有在INFO
級别上的日志,或者被DEBUG
django.request
處理的日志)。django.security
-
logger,它處理來自py.warnings
的消息,會向控制台發送消息。warnings.warn()
DEBUG
False
-
django.request
loggers 向django.security
發送帶有AdminEmailHandler
或ERROR
級别的消息。這些logger 會忽略任何級别等于或小于CRITICAL
的資訊,被記錄的日志不會傳遞給其他logger(它們不會傳遞給WARNING
的全局 logger,即使django
DEBUG
)。True
另見
配置日志來了解如何補充或者替換預設的日志配置。
譯者: Django 文檔協作翻譯小組 ,原文: Logging 本文以 CC BY-NC-SA 3.0 協定釋出,轉載請保留作者署名和文章出處。 人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。