Python 程序員通常會在其代碼中使用?print()
?作為一種快速和方便的調試工具。使用日志框架只比這多花一點點工夫,但更加優雅和靈活。除了用于調試之外,日志還可以為您提供有關應用程序狀態和健康狀況的更多信息,而且這些信息結構更清晰。
一、概況
Django 使用并擴展了 Python 內置的 logging 模塊來執行系統日志記錄。這個模塊在 Python 自己的文檔中有詳細的討論;這一部分提供了一個快速概覽。
1、日志框架的組成元素
一份 Python logging 配置有下面四個部分組成:
- Loggers
- Handlers
- 過濾器
- Formatters
1.1 Loggers
logger?是日志系統的入口點。每個 logger 是一個命名的容器,可以將消息寫入其中以進行處理。
logger 可以配置?日志級別。日志級別描述了由該 logger 處理的消息的嚴重性。Python 定義了下面幾種日志級別:
-
DEBUG
:排查故障時使用的低級別系統信息 -
INFO
:一般的系統信息 -
WARNING
:描述系統發生了一些小問題的信息 -
ERROR
:描述系統發生了大問題的信息 -
CRITICAL
:描述系統發生嚴重問題的信息
每一條寫入 logger 的消息都是一條?日志記錄。每一條日志記錄也包含?日志級別,代表對應消息的嚴重程度。日志記錄還包含有用的元數據,來描述被記錄了日志的事件細節,例如堆棧跟蹤或者錯誤碼。
當 logger 處理一條消息時,會將自己的日志級別和這條消息的日志級別做對比。如果消息的日志級別匹配或者高于 logger 的日志級別,它就會被進一步處理。否則這條消息就會被忽略掉。
當 logger 確定了一條消息需要處理之后,會把它傳給?Handler。
1.2?Handlers
handler?是確定 logger 中每個消息發生什么的引擎。它描述了特定的日志行為,例如將消息寫入屏幕、寫入文件或寫入網絡套接字。
和 logger 一樣,handler 也有日志級別的概念。如果一條日志記錄的級別不匹配或者低于 handler 的日志級別,對應的消息會被 handler 忽略。
一個 logger 可以有多個 handler,每一個 handler 可以有不同的日志級別。這樣就可以根據消息的重要性不同,來提供不同格式的輸出。例如,你可以添加一個 handler 把?ERROR
?和?CRITICAL
?消息發到尋呼機,再添加另一個 handler 把所有的消息(包括?ERROR
?和?CRITICAL
?消息)保存到文件里以便日后分析。
1.3 過濾器
filter?用于在從 logger 傳遞到 handler 的日志記錄中提供額外的控制。
默認情況下,只要級別匹配,任何日志消息都會被處理。不過,也可以通過添加 filter 來給日志處理的過程增加額外條件。例如,可以添加一個 filter 只允許某個特定來源的?ERROR
?消息輸出。
Filter 還被用來在日志輸出之前對日志記錄做修改。例如,可以寫一個 filter,當滿足一定條件時,把日志記錄從?ERROR
?降到?WARNING
?級別。
Filter 在 logger 和 handler 中都可以添加;多個 filter 可以鏈接起來使用,來做多重過濾操作。
1.4 Formatters
最終,日志記錄需要呈現為文本。Formatter 描述了文本的精確格式。Formatter 通常由一個包含 LogRecord 屬性 的 Python 格式化字符串組成;但是,您也可以編寫自定義的 Formatter 來實現特定的格式化行為。
二、安全性考慮
日志記錄系統處理可能包含敏感信息的數據。例如,日志記錄可能包含有關 Web 請求或堆棧跟蹤的信息,而您自己的日志記錄中收集的一些數據也可能具有安全性影響。您需要確保自己明白以下事項:
-
會收集什么信息
-
日志記錄將隨后存儲在何處。
-
它將如何傳輸。
-
誰可能訪問它
為了幫助控制敏感信息的收集,您可以明確指定某些敏感信息在錯誤報告中被過濾掉,詳細了解如何?過濾錯誤報告。
1、AdminEmailHandler
在安全性的背景下,應該提到內置的?AdminEmailHandler。如果啟用了它的?include_html?選項,它發送的電子郵件消息將包含完整的回溯信息,以及堆棧的每個級別的本地變量的名稱和值,以及您的 Django 設置的值(換句話說,與在?DEBUG?為?True?時在 Web 頁面中顯示的詳細程度相同)。
通常不建議通過電子郵件發送此類可能包含敏感信息的信息。相反,考慮使用其中許多第三方服務之一,將詳細日志發送到這些服務,以獲得多個優勢:完整回溯信息的豐富信息、清晰的通知和訪問信息的管理等等。這樣可以更好地處理敏感信息。
三、日志模塊的配置
Python 的日志庫提供了一些配置方法,可以使用編程接口或者配置文件。Django默認使用?dictConfig format。
要配置日志記錄,您使用?LOGGING?來定義一個日志設置字典。這些設置描述了您希望在日志設置中使用的記錄器、處理程序、過濾器和格式化程序,以及您希望這些組件具有的日志級別和其他屬性。
默認情況下?LOGGING?配置和?Django 默認日志配置?按照下面的方式合并在一起:
如果?LOGGING?dictConfig 中的?disable_existing_loggers?鍵被設置為?True?(如果該鍵缺失,則為?dictConfig?默認值),則默認配置中的所有記錄器都將被禁用。禁用的記錄器與刪除的記錄器不同;記錄器仍將存在,但會默默地丟棄任何記錄到它的內容,甚至不會將條目傳播到父記錄儀。因此,你應該非常小心地使用?'disable_existing_loggers':?True;這可能不是你想要的。相反,你可以將?disable_existing_loggers?設置為?False,然后重新定義一些或所有的默認日志記錄器;或者你可以將?LOGGING_CONFIG?設置為?None,然后?自己處理日志配置。
logging 被配置成了 Django?setup()?函數的一部分。因此,你可以確定的是,logger 一直都可以在項目代碼里使用。
1、示例
?dictConfig format文檔是獲取日志配置細節的最好資料。不過,為了讓你知道能做什么,下面有幾個例子。
首先,這里有一個小配置,可以讓你把所有的日志信息輸出到控制臺。
import logging
import mylib
logger = logging.getLogger(__name__)def main():logging.basicConfig(filename='myapp.log', level=logging.INFO)logger.info('Started')mylib.do_something()logger.info('Finished')if __name__ == '__main__':main()
???????
?
settings.pyimport osLOGGING = {"version": 1,"disable_existing_loggers": False,"handlers": {"console": {"class": "logging.StreamHandler",},},"root": {"handlers": ["console"],"level": "WARNING",},
}
?
這將配置父?root
?記錄器,以向控制臺處理程序發送?WARNING
?級別及以上的消息。通過將級別調整為?INFO
?或?DEBUG
,可以顯示更多的消息。這在開發過程中可能很有用。
接下來我們可以添加更多細粒度的日志記錄。下面是一個例子,說明如何讓日志系統只從名為 logger 的?django?中打印更多的消息。
?
settings.pyimport osLOGGING = {"version": 1,"disable_existing_loggers": False,"handlers": {"console": {"class": "logging.StreamHandler",},},"root": {"handlers": ["console"],"level": "WARNING",},"loggers": {"django": {"handlers": ["console"],"level": os.getenv("DJANGO_LOG_LEVEL", "INFO"),"propagate": False,},},
}
?
默認情況下,這個配置會從?django
?的日志記錄器中發送級別為?INFO
?或更高的消息到控制臺。這個級別和 Django 的默認日志配置是一樣的,只是默認配置只在?DEBUG=True
?時才顯示日志記錄。Django 不會記錄很多這樣的?INFO
?級別的消息。不過,有了這個配置,你也可以設置環境變量?DJANGO_LOG_LEVEL=DEBUG
?來查看 Django 所有的調試日志,因為它包括了所有的數據庫查詢,所以非常啰嗦。
你不需要把日志記錄到控制臺。下面是一個配置,它將所有來自?django?命名的記錄器的日志記錄寫入本地文件。
settings.pyLOGGING = {"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,},},
}
若你使用此例子,切記要將?'filename'
?指向的路徑改為當前運行 Django 應用的用戶可寫的路徑。
最后,這里是一個相當復雜的日志設置的例子。
settings.pyLOGGING = {"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"],},},
}
該日志配置做了以下事情:
-
識別配置為 'dictConfig 版本 1' 格式。目前,這是唯一的 dictConfig 格式版本。
-
定義兩個格式化程序:
-
simple
,輸出日志級別名稱(如?DEBUG
)和日志信息。format
?字符串是一個普通的 Python 格式化字符串,它描述了每個日志行要輸出的細節。可以輸出的完整細節列表可以在?Formatter Objects?中找到。 -
verbose
,輸出日志級別名稱、日志信息,以及生成日志信息的時間、進程、線程和模塊。
-
-
定義兩個過濾器:
-
project.logging.SpecialFilter
,使用別名?special
。如果這個過濾器需要額外的參數,它們可以作為過濾器配置字典中的附加鍵提供。在這種情況下,當實例化?SpecialFilter
?時,參數?foo
?將被賦予一個?bar
?的值。 -
django.utils.log.RequireDebugTrue
,當?DEBUG?為?True?時,傳遞記錄。
-
-
定義兩個處理程序:
-
console
,一個?StreamHandler,它將任何?INFO
?(或更高)消息打印到?sys.stderr
。該處理程序使用?simple
?輸出格式。 -
mail_admins
?是一個?AdminEmailHandler,它會將任何?ERROR?(或更高級別)的消息通過電子郵件發送給站點的?ADMINS。
這個處理程序使用了?special
?過濾器。
-
-
配置三個記錄器。
-
django
,將所有信息傳遞給?console
?處理程序。 -
django.request
,它將所有?ERROR
?消息傳遞給?mail_admins
?處理程序。此外,這個記錄器被標記為?不?傳播消息。這意味著寫給?django.request
?的日志信息不會被?django
?日志處理程序處理。 -
myproject.custom
,它將所有?INFO
?或更高等級的消息傳遞給兩個處理程序——console
?和?mail_admins
。這意味著所有?INFO
?級別(或更高)的消息將被打印到控制臺;ERROR
?和?CRITICAL
?消息也將通過電子郵件輸出。
-
2、自定義日志記錄配置
如果你不想使用 Python 的 dictConfig 格式來配置記錄器,你可以指定自己的配置方案。
LOGGING_CONFIG?設置定義了用于配置 Django 日志記錄器的可調用對象,默認情況下,它指向 Python 的?logging.config.dictConfig()?函數。然而,如果你想使用不同的配置過程,你可以使用其他任何一個接受單一參數的可調用。當配置日志時,?LOGGING?的內容將作為該參數的值提供。
?
3、禁用日志記錄配置
如果你根本不想配置日志記錄(或者你想用自己的方法手動配置日志記錄),你可以將?LOGGING_CONFIG?設置為?None。這將禁用?Django 的默認日志記錄?的配置過程。
將?LOGGING_CONFIG?設置為?None?只是意味著自動配置過程被禁用,而不是日志本身。如果你禁用了配置過程,Django 仍然會進行日志調用,回到默認的日志行為。
下面是一個禁用 Django 的日志配置,然后手動配置日志的例子。
settings.pyLOGGING_CONFIG = Noneimport logging.configlogging.config.dictConfig(...)
請注意,默認的配置過程只有在設置完全加載后才會調用 LOGGING_CONFIG。相反,在設置文件中手動配置日志記錄將立即加載你的日志記錄配置。因此,你的日志配置必須出現在它所依賴的任何設置之后。