Django 使用Python 內(nèi)建的logging 模塊打印日志。該模塊的用法在Python 本身的文檔中有詳細(xì)的討論。如果你從來沒有使用過Python 的logging 框架(或者即使使用過),請(qǐng)參見下面的快速導(dǎo)論。
Python 的logging 配置由四個(gè)部分組成:
Logger 為日志系統(tǒng)的入口。每個(gè)logger 是一個(gè)具名的容器,可以向它寫入需要處理的消息。
每個(gè)logger 都有一個(gè)日志級(jí)別。日志級(jí)別表示該logger 將要處理的消息的嚴(yán)重性。Python 定義以下幾種日志級(jí)別:
DEBUG:用于調(diào)試目的的底層系統(tǒng)信息INFO:普通的系統(tǒng)信息WARNING:表示出現(xiàn)一個(gè)較小的問題。ERROR:表示出現(xiàn)一個(gè)較大的問題。CRITICAL:表示出現(xiàn)一個(gè)致命的問題。寫入logger 的每條消息都是一個(gè)日志記錄。每個(gè)日志記錄也具有一個(gè)日志級(jí)別,它表示對(duì)應(yīng)的消息的嚴(yán)重性。每個(gè)日志記錄還可以包含描述正在打印的事件的有用元信息。這些元信息可以包含很多細(xì)節(jié),例如回溯棧或錯(cuò)誤碼。
當(dāng)給一條消息給logger 時(shí),會(huì)將消息的日志級(jí)別與logger 的日志級(jí)別進(jìn)行比較。如果消息的日志級(jí)別大于等于logger 的日志級(jí)別,該消息將會(huì)往下繼續(xù)處理。如果小于,該消息將被忽略。
Logger 一旦決定消息需要處理,它將傳遞該消息給一個(gè)Handler。
Handler 決定如何處理logger 中的每條消息。它表示一個(gè)特定的日志行為,例如將消息寫到屏幕上、寫到文件中或者寫到網(wǎng)絡(luò)socket。
與logger 一樣,handler 也有一個(gè)日志級(jí)別。如果消息的日志級(jí)別小于handler 的級(jí)別,handler 將忽略該消息。
Logger 可以有多個(gè)handler,而每個(gè)handler 可以有不同的日志級(jí)別。利用這種方式,可以根據(jù)消息的重要性提供不同形式的處理。例如,你可以用一個(gè)handler 將ERROR 和 CRITICAL 消息發(fā)送給一個(gè)頁(yè)面服務(wù),而用另外一個(gè)hander 將所有的消息(包括 ERROR 和CRITICAL 消息)記錄到一個(gè)文件中用于以后進(jìn)行分析。
Filter 用于對(duì)從logger 傳遞給handler 的日志記錄進(jìn)行額外的控制。
默認(rèn)情況下,滿足日志級(jí)別的任何消息都將被處理。通過安裝一個(gè)filter,你可以對(duì)日志處理添加額外的條件。例如,你可以安裝一個(gè)filter,只允許處理來自特定源的ERROR 消息。
Filters 還可以用于修改將要處理的日志記錄的優(yōu)先級(jí)。例如,如果日志記錄滿足特定的條件,你可以編寫一個(gè)filter 將日志記錄從ERROR 降為WARNING。
Filters 可以安裝在logger 上或者h(yuǎn)andler 上;多個(gè)filter 可以串聯(lián)起來實(shí)現(xiàn)多層filter 行為。
最后,日志記錄需要轉(zhuǎn)換成文本。Formatter 表示文本的格式。Fomatter 通常由包含日志記錄屬性的Python 格式字符串組成;你也可以編寫自定義的fomatter 來實(shí)現(xiàn)自己的格式。
配置好logger、handler、filter 和formatter 之后,你需要在代碼中放入logging 調(diào)用。使用logging 框架非常簡(jiǎn)單。下面是個(gè)例子:
# 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 條件,將寫入一條錯(cuò)誤日志記錄。
logging.getLogger() 調(diào)用獲取(如有必要?jiǎng)t創(chuàng)建)一個(gè)logger 的實(shí)例。Logger 實(shí)例通過名字標(biāo)識(shí)。Logger 使用名稱的目的是用于標(biāo)識(shí)其配置。
Logger 的名稱習(xí)慣上通常使用__name__,即包含該logger 的Python 模塊的名字。這允許你基于模塊filter 和handle 日志調(diào)用。如果你想使用其它方式組織日志消息,可以提供點(diǎn)號(hào)分隔的名稱來標(biāo)識(shí)你的logger:
# Get an instance of a specific named logger
logger = logging.getLogger('project.interesting.stuff')
點(diǎn)號(hào)分隔的logger 名稱定義一個(gè)層級(jí)。project.interesting logger 被認(rèn)為是 project.interesting.stuff logger 的上一級(jí);project logger 是project.interesting logger 的上一級(jí)。
層級(jí)為何如此重要?因?yàn)榭梢栽O(shè)置logger _傳播_它們的logging 調(diào)用給它們的上一級(jí)。利用這種方式,你可以在根logger 上定義一系列的handler,并捕獲子logger 中的所有l(wèi)ogging 調(diào)用。在project命名空間中定義的handler 將捕獲project.interesting 和project.interesting.stuff logger 上的所有日志消息。
這種傳播行為可以基于每個(gè)logger 進(jìn)行控制。如果你不想讓某個(gè)logger 傳播消息給它的上一級(jí),你可以關(guān)閉這個(gè)行為。
Logger 實(shí)例為每個(gè)默認(rèn)的日志級(jí)別提供一個(gè)入口方法:
logger.debug()logger.info()logger.warning()logger.error()logger.critical()還有另外兩個(gè)調(diào)用:
logger.log():打印消息時(shí)手工指定日志級(jí)別。logger.exception():創(chuàng)建一個(gè)ERROR 級(jí)別日志消息,它封裝當(dāng)前異常棧的幀。當(dāng)然,只是將logging 調(diào)用放入你的代碼中還是不夠的。你還需要配置logger、handler、filter 和formatter 來確保日志的輸出是有意義的。
Python 的logging 庫(kù)提供幾種配置logging 的技術(shù),從程序接口到配置文件。默認(rèn)情況下,Django 使用dictConfig 格式。
為了配置logging,你需要使用LOGGING 來定義字典形式的logging 設(shè)置。這些設(shè)置描述你的logging 設(shè)置的logger、handler、filter 和formatter,以及它們的日志等級(jí)和其它屬性。
默認(rèn)情況下,LOGGING 設(shè)置與Django 的默認(rèn)logging 配置進(jìn)行合并。
如果LOGGING 中的disable_existing_loggers 鍵為True(默認(rèn)值),那么默認(rèn)配置中的所有l(wèi)ogger 都將禁用。Logger 的禁用與刪除不同;logger 仍然存在,但是將默默丟棄任何傳遞給它的信息,也不會(huì)傳播給上一級(jí)logger。所以,你應(yīng)該非常小心使用'disable_existing_loggers': True;它可能不是你想要的。你可以設(shè)置disable_existing_loggers 為False,并重新定義部分或所有的默認(rèn)loggers;或者你可以設(shè)置LOGGING_CONFIG 為 None,并 自己處理logging 配置。
Logging 的配置屬于Django setup() 函數(shù)的一部分。所以,你可以肯定在你的項(xiàng)目代碼中l(wèi)ogger 是永遠(yuǎn)可用的。
dictConfig 格式的完整文檔是logging 字典配置最好的信息源。但是為了讓你嘗嘗,下面是幾個(gè)例子。
首先,下面是一個(gè)簡(jiǎn)單的配置,它將來自django.request logger 的所有日志請(qǐng)求寫入到一個(gè)本地文件:
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,
},
},
}
如果你使用這個(gè)示例,請(qǐng)確保修改'filename' 路徑為運(yùn)行Django 應(yīng)用的用戶有權(quán)限寫入的一個(gè)位置。
其次,下面這個(gè)示例演示如何讓日志系統(tǒng)將Django 的日志打印到控制臺(tái)。django.request 和django.security 不會(huì)傳播日志給上一級(jí)。它在本地開發(fā)期間可能有用。
默認(rèn)情況下,這個(gè)配置只會(huì)將INFO 和更高級(jí)別的日志發(fā)送到控制臺(tái)。Django 中這樣的日志信息不多??梢栽O(shè)置環(huán)境變量DJANGO_LOG_LEVEL=DEBUG 來看看Django 的debug 日志,它包含所有的數(shù)據(jù)庫(kù)查詢所以非常詳盡。
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'),
},
},
}
最后,下面是相當(dāng)復(fù)雜的一個(gè)logging 設(shè)置:
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']
}
}
}
這個(gè)logging 配置完成以下事情:
以‘dictConfig version 1’格式解析配置。目前為止,這是dictConfig 格式唯一的版本。
定義兩個(gè)formatter:
simple,它只輸出日志的級(jí)別(例如,DEBUG)和日志消息。
`format` 字符串是一個(gè)普通的Python 格式化字符串,描述每行日志的細(xì)節(jié)。輸出的完整細(xì)節(jié)可以在[formatter 文檔](https://docs.python.org/library/logging.html#formatter-objects)中找到。
verbose,它輸出日志級(jí)別、日志消息,以及時(shí)間、進(jìn)程、線程和生成日志消息的模塊。定義filter —— project.logging.SpecialFilter,并使用別名special。如果filter 在構(gòu)造時(shí)要求額外的參數(shù),可以在filter 的配置字段中用額外的鍵提供。在這個(gè)例子中,在實(shí)例化SpecialFilter 時(shí),foo 參數(shù)的值將使用bar。
定義三個(gè)handler:
null,一個(gè)NullHandler,它傳遞DEBUG(和更高級(jí))的消息給/dev/null。console,一個(gè)StreamHandler,它將打印DEBUG(和更高級(jí))的消息到stderr。這個(gè)handler 使用simple 輸出格式。mail_admins,一個(gè)AdminEmailHandler,它將用郵件發(fā)送ERROR(和更高級(jí))的消息到站點(diǎn)管理員。這個(gè)handler 使用special filter。配置三個(gè)logger:
django,它傳遞所有INFO 和更高級(jí)的消息給null handler。django.request,它傳遞所有ERROR 消息給mail_admins handler。另外,標(biāo)記這個(gè)logger 不 向上傳播消息。這表示寫入django.request 的日志信息將不會(huì)被django logger 處理。myproject.custom,它傳遞所有INFO 和更高級(jí)的消息并通過special filter 的消息給兩個(gè)handler —— console和mail_admins。這表示所有INFO(和更高級(jí))的消息將打印到控制臺(tái)上;ERROR 和CRITICAL 消息還會(huì)通過郵件發(fā)送出來。如果你不想使用Python 的dictConfig 格式配置logger,你可以指定你自己的配置模式。
LOGGING_CONFIG 設(shè)置定義一個(gè)可調(diào)用對(duì)象,將它用來配置Django 的logger。默認(rèn)情況下,它指向Python 的logging.config.dictConfig() 函數(shù)。但是,如果你想使用不同的配置過程,你可以使用其它只接受一個(gè)參數(shù)的可調(diào)用對(duì)象。配置logging 時(shí),將使用LOGGING 的內(nèi)容作為參數(shù)的值。
如果你完全不想配置logging(或者你想使用自己的方法手工配置logging),你可以設(shè)置LOGGING_CONFIG 為None。這將禁用Django 默認(rèn)logging 的配置過程。下面的示例禁用Django 的logging 配置,然后手工配置logging:
settings.py
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
設(shè)置LOGGING_CONFIG 為None 只表示禁用自動(dòng)配置過程,而不是禁用logging 本身。如果你禁用配置過程,Django 仍然執(zhí)行l(wèi)ogging 調(diào)用,只是調(diào)用的是默認(rèn)定義的logging 行為。
Django 提供許多工具用于處理在網(wǎng)站服務(wù)器環(huán)境中獨(dú)特的日志需求。
Django 提供幾個(gè)內(nèi)建的logger。
django 是一個(gè)捕獲所有信息的logger。消息不會(huì)直接提交給這個(gè)logger。
記錄與處理請(qǐng)求相關(guān)的消息。5XX 響應(yīng)作為ERROR 消息;4XX 響應(yīng)作為WARNING 消息。
這個(gè)logger 的消息具有以下額外的上下文:
status_code:請(qǐng)求的HTTP 響應(yīng)碼。request:生成日志信息的請(qǐng)求對(duì)象。與數(shù)據(jù)庫(kù)交互的代碼相關(guān)的消息。例如,HTTP請(qǐng)求執(zhí)行應(yīng)用級(jí)別的SQL 語句將以DEBUG 級(jí)別記錄到該logger。
這個(gè)logger 的消息具有以下額外的上下文:
duration:執(zhí)行SQL 語句花費(fèi)的時(shí)間。sql:執(zhí)行的SQL 語句。params:SQL 調(diào)用中用到的參數(shù)。由于性能原因,SQL的日志只在設(shè)置之后開啟。DEBUG 設(shè)置為True,無論日志級(jí)別或者安裝的處理器是什么。
這里的日志不包含框架級(jí)別的的初始化(例如,SET TIMEZONE)和事務(wù)管理查詢(例如,BEGIN、COMMIT 和ROLLBACK)。如果你希望看到所有的數(shù)據(jù)庫(kù)查詢,可以打開數(shù)據(jù)庫(kù)中的查詢?nèi)罩尽?/p>
Security logger 將收到任何出現(xiàn)SuspiciousOperation 的消息。SuspiciousOperation 的每個(gè)子類型都有一個(gè)子logger。日志的級(jí)別取決于異常處理的位置。大部分情況是一個(gè)warning 日志,而如果SuspiciousOperation 到達(dá)WSGI handler 則記錄為一個(gè)error。例如,如果請(qǐng)求中包含的HTTP Host 頭部與ALLOWED_HOSTS 不匹配,Django 將返回400 響應(yīng),同時(shí)將記錄一個(gè)error 消息到django.security.DisallowedHost logger。
默認(rèn)情況下只會(huì)配置django.security logger,其它所有的子logger 都將傳播給上一級(jí)logger。django.security logger 的配置與django.request logger 相同,任何error 消息將用郵件發(fā)送給站點(diǎn)管理員。由于SuspiciousOperation 導(dǎo)致400 響應(yīng)的請(qǐng)求不會(huì)在django.request logger 中記錄日志,而只在django.security logger 中記錄日志。
若要默默丟棄某種類型的SuspiciousOperation,你可以按照下面的示例覆蓋其logger:
'loggers': {
'django.security.DisallowedHost': {
'handlers': ['null'],
'propagate': False,
},
},
New in Django 1.7.
當(dāng)遷移框架執(zhí)行的SQL 查詢會(huì)改變數(shù)據(jù)庫(kù)的模式時(shí),則記錄這些SQL 查詢。注意,它不會(huì)記錄RunPython 執(zhí)行的查詢。
在Python logging 模塊提供的handler 基礎(chǔ)之上,Django 還提供另外一個(gè)handler。
class AdminEmailHandler(_includehtml=False, _emailbackend=None)[source]
這個(gè)handler 將它收到的每個(gè)日志信息用郵件發(fā)送給站點(diǎn)管理員。
如果日志記錄包含request 屬性,該請(qǐng)求的完整細(xì)節(jié)都將包含在郵件中。
如果日志記錄包含?;厮菪畔?,該棧回溯也將包含在郵件中。
AdminEmailHandler 的include_html 參數(shù)用于控制郵件中是否包含HTML 附件,這個(gè)附件包含DEBUG 為True 時(shí)的完整網(wǎng)頁(yè)。若要在配置中設(shè)置這個(gè)值,可以將它包含在django.utils.log.AdminEmailHandler handler 的定義中,像下面這樣:
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
}
},
注意,郵件中的HTML 包含完整的回溯棧,包括棧每個(gè)層級(jí)局部變量的名稱和值以及你的Django 設(shè)置。這些信息可能非常敏感,你也許不想通過郵件發(fā)送它們。此時(shí)可以考慮使用類似Sentry 這樣的東西,回溯棧的完整信息和安全信息不會(huì) 通過郵件發(fā)送。你還可以從錯(cuò)誤報(bào)告中顯式過濾掉特定的敏感信息 —— 更多信息參見過濾錯(cuò)誤報(bào)告。
通過設(shè)置AdminEmailHandler 的email_backend 參數(shù),可以覆蓋handler 使用的email backend,像這樣:
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
}
},
默認(rèn)情況下,將使用EMAIL_BACKEND 中指定的郵件后端。
send_mail(subject, message, *args, **kwargs)[source]
New in Django 1.8.
發(fā)送郵件給管理員用戶。若要自定它的行為,可以子類化AdminEmailHandler 類并覆蓋這個(gè)方法。
在Python logging 模塊提供的過濾器的基礎(chǔ)之上,Django 還提供兩個(gè)過濾器。
class CallbackFilter(callback)[source]
這個(gè)過濾器接受一個(gè)回調(diào)函數(shù)(它接受一個(gè)單一參數(shù),也就是要記錄的東西),并且對(duì)每個(gè)傳遞給過濾器的記錄調(diào)用它。如果回調(diào)函數(shù)返回False,將不會(huì)進(jìn)行記錄的處理。
例如,要從admin郵件中過濾掉UnreadablePostError(只在用戶取消上傳時(shí)產(chǎn)生),你可以創(chuàng)建一個(gè)過濾器函數(shù):
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]
這個(gè)過濾器只在設(shè)置后傳遞記錄。DEBUG 為 False。
這個(gè)過濾器遵循LOGGING 默認(rèn)的配置,以確保AdminEmailHandler只在DEBUG為False的時(shí)候發(fā)送錯(cuò)誤郵件。
'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]
這個(gè)過濾器類似于RequireDebugFalse,除了記錄只在DEBUG 為 True時(shí)傳遞的情況。
默認(rèn)情況下,Django 的logging 配置如下:
當(dāng)DEBUG 為True 時(shí):
django的全局logger會(huì)向控制臺(tái)發(fā)送級(jí)別等于或高級(jí)INFO的所有消息。Django在這個(gè)時(shí)候并不會(huì)做任何日志調(diào)用(所有在DEBUG級(jí)別上的日志,或者被django.request 和 django.security處理的日志)。py.warnings logger,它處理來自warnings.warn()的消息,會(huì)向控制臺(tái)發(fā)送消息。當(dāng)DEBUG 為False 時(shí):
django.request 和django.security loggers 向AdminEmailHandler發(fā)送帶有ERROR 或 CRITICAL級(jí)別的消息。這些logger 會(huì)忽略任何級(jí)別等于或小于WARNING的信息,被記錄的日志不會(huì)傳遞給其他logger(它們不會(huì)傳遞給django的全局 logger,即使DEBUG 為 True)。另見配置日志來了解如何補(bǔ)充或者替換默認(rèn)的日志配置。
譯者:Django 文檔協(xié)作翻譯小組,原文:Logging。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。