日誌

Python 程式設計師通常會在他們的程式碼中使用 print() 作為快速且方便的除錯工具。使用日誌框架僅比使用 print() 多花一點力氣,但它更加優雅且具有彈性。除了用於除錯外,日誌還可以為您提供更多 - 以及結構更好的 - 關於應用程式狀態和健康狀況的資訊。

總覽

Django 使用並擴展了 Python 的內建 logging 模組來執行系統日誌。此模組在 Python 自己的文件中進行了詳細討論;本節提供一個快速的概述。

參與者

Python 日誌設定由四個部分組成

日誌記錄器

日誌記錄器是進入日誌系統的入口點。每個日誌記錄器都是一個命名的儲存桶,訊息可以寫入其中進行處理。

日誌記錄器被設定為具有日誌級別。此日誌級別描述了日誌記錄器將處理的訊息的嚴重程度。Python 定義了以下日誌級別

  • DEBUG:用於除錯目的的低級別系統資訊

  • INFO:一般系統資訊

  • WARNING:描述已發生次要問題的資訊。

  • ERROR:描述已發生主要問題的資訊。

  • CRITICAL:描述已發生嚴重問題的資訊。

寫入日誌記錄器的每個訊息都是一個日誌記錄。每個日誌記錄也有一個日誌級別,表示該特定訊息的嚴重程度。日誌記錄還可以包含描述正在記錄的事件的有用元數據。這可以包括諸如堆疊追蹤或錯誤代碼之類的詳細資訊。

當將訊息給予日誌記錄器時,會將訊息的日誌級別與日誌記錄器的日誌級別進行比較。如果訊息的日誌級別達到或超過日誌記錄器本身的日誌級別,則訊息將進行進一步處理。如果沒有,則將忽略該訊息。

一旦日誌記錄器確定需要處理訊息,就會將其傳遞給處理器

處理器

處理器是決定日誌記錄器中每個訊息發生情況的引擎。它描述特定的日誌行為,例如將訊息寫入螢幕、檔案或網路插槽。

與日誌記錄器一樣,處理器也有一個日誌級別。如果日誌記錄的日誌級別未達到或超過處理器的級別,則處理器將忽略該訊息。

一個日誌記錄器可以有多個處理器,並且每個處理器可以具有不同的日誌級別。這樣,可以根據訊息的重要性提供不同形式的通知。例如,您可以安裝一個將 ERRORCRITICAL 訊息轉發到呼叫服務的處理器,而第二個處理器則將所有訊息(包括 ERRORCRITICAL 訊息)記錄到檔案中以供稍後分析。

篩選器

篩選器用於提供對哪些日誌記錄從日誌記錄器傳遞到處理器的額外控制。

預設情況下,任何符合日誌級別要求的日誌訊息都將被處理。但是,通過安裝篩選器,您可以在日誌記錄過程中設定額外條件。例如,您可以安裝一個篩選器,該篩選器僅允許發出特定來源的 ERROR 訊息。

篩選器還可以用於在發出日誌記錄之前對其進行修改。例如,如果滿足一組特定條件,您可以編寫一個篩選器,將 ERROR 日誌記錄降級為 WARNING 記錄。

篩選器可以安裝在日誌記錄器或處理器上;可以在鏈中使用多個篩選器來執行多個篩選動作。

格式器

最終,日誌記錄需要以文字形式呈現。格式器描述了該文字的確切格式。格式器通常由包含 LogRecord 屬性的 Python 格式字串組成;但是,您也可以編寫自訂格式器來實現特定的格式化行為。

安全隱憂

日誌系統處理潛在的敏感資訊。例如,日誌記錄可能包含有關網頁請求或堆疊追蹤的資訊,而您在自己的日誌記錄器中收集的一些資料也可能具有安全隱憂。您需要確保您知道

  • 收集哪些資訊

  • 隨後將儲存在哪裡

  • 將如何傳輸

  • 誰可以存取它。

為了幫助控制敏感資訊的收集,您可以明確指定某些敏感資訊從錯誤報告中篩除 – 閱讀更多關於如何 篩選錯誤報告 的資訊。

AdminEmailHandler

在安全性的背景下,內建的 AdminEmailHandler 值得一提。如果啟用了其 include_html 選項,則其傳送的電子郵件訊息將包含完整的追溯,其中包含堆疊每一層的局部變數的名稱和值,以及 Django 設定的值(換句話說,與當 DEBUGTrue 時在網頁中公開的詳細程度相同)。

通過電子郵件傳送如此潛在的敏感資訊通常不被認為是一個好主意。請考慮使用許多第三方服務之一,可以將詳細的日誌傳送到這些服務,以獲得多個世界的最佳效果 – 完整追溯的豐富資訊、對誰收到通知和有權存取資訊的清晰管理等等。

設定日誌

Python 的日誌庫提供了幾種設定日誌的技術,從程式設計介面到設定檔案不等。預設情況下,Django 使用 dictConfig 格式

為了設定日誌,您可以使用 LOGGING 來定義日誌設定的字典。這些設定描述了您想要在日誌設定中使用的日誌記錄器、處理器、篩選器和格式器,以及您希望這些元件擁有的日誌級別和其他屬性。

預設情況下,LOGGING 設定與 Django 的預設日誌設定 使用以下方案合併。

如果在 LOGGING dictConfig 中的 disable_existing_loggers 鍵設定為 True(如果缺少該鍵,則這是 dictConfig 的預設值),則預設設定中的所有日誌記錄器都將被停用。停用的日誌記錄器與刪除的不同;日誌記錄器仍然存在,但會靜默地丟棄記錄到它的任何內容,甚至不會將條目傳播到父日誌記錄器。因此,您應該非常小心地使用 'disable_existing_loggers': True;這可能不是您想要的。相反,您可以將 disable_existing_loggers 設定為 False 並重新定義某些或所有預設的日誌記錄器;或者您可以將 LOGGING_CONFIG 設定為 None自行處理日誌設定

日誌記錄設定是 Django setup() 函式整體設定的一部分。因此,您可以確定記錄器始終可以在您的專案程式碼中使用。

範例

關於日誌記錄設定字典的完整文件,最好的資訊來源是 dictConfig 格式。不過,為了讓您了解可能的用途,這裡提供幾個範例。

首先,這裡有一個小型設定,可讓您將所有日誌訊息輸出到主控台

settings.py
import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
}

此設定將父記錄器 root 設定為將 WARNING 等級或更高等級的訊息傳送到主控台處理器。您可以調整等級為 INFODEBUG 來顯示更多訊息。這在開發期間可能會很有用。

接下來,我們可以新增更細緻的日誌記錄。以下範例說明如何讓日誌系統僅印出來自名為 django 的記錄器的更多訊息

settings.py
import os

LOGGING = {
    "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.py
LOGGING = {
    "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.py
LOGGING = {
    "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 version 1' 格式。目前,這是唯一的 dictConfig 格式版本。

  • 定義兩個格式器

    • simple,輸出日誌等級名稱 (例如,DEBUG) 和日誌訊息。

      format 字串是一個普通的 Python 格式化字串,用於描述要在每個日誌記錄行上輸出的詳細資訊。可以輸出的完整詳細資訊清單可以在 格式器物件 中找到。

    • verbose,輸出日誌等級名稱、日誌訊息,以及產生日誌訊息的時間、處理序、執行緒和模組。

  • 定義兩個篩選器

    • project.logging.SpecialFilter,使用別名 special。如果此篩選器需要其他引數,則可以在篩選器設定字典中以其他鍵的形式提供。在這種情況下,在具現化 SpecialFilter 時,引數 foo 將會被賦予 bar 的值。

    • django.utils.log.RequireDebugTrue,當 DEBUGTrue 時,會傳遞記錄。

  • 定義兩個處理器

    • 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 或更高,且也通過 special 篩選器的訊息傳遞到兩個處理器 - consolemail_admins。這表示所有 INFO 等級的訊息 (或更高) 都會列印到主控台;ERRORCRITICAL 訊息也會透過電子郵件輸出。

自訂日誌記錄設定

如果您不想使用 Python 的 dictConfig 格式來設定記錄器,您可以指定自己的設定方式。

LOGGING_CONFIG 設定定義了將用於設定 Django 記錄器的可呼叫物件。預設情況下,它指向 Python 的 logging.config.dictConfig() 函式。但是,如果您想要使用不同的設定程序,則可以使用任何其他接受單一引數的可呼叫物件。LOGGING 的內容會在設定日誌記錄時作為該引數的值提供。

停用日誌記錄設定

如果您完全不想設定日誌記錄 (或者您想要使用自己的方法手動設定日誌記錄),您可以將 LOGGING_CONFIG 設定為 None。這將停用 Django 的預設日誌記錄的設定程序。

LOGGING_CONFIG 設定為 None 僅表示自動設定程序已停用,而不是日誌記錄本身。如果您停用設定程序,Django 仍會進行日誌記錄呼叫,並回到定義的任何預設日誌記錄行為。

以下範例停用 Django 的日誌記錄設定,然後手動設定日誌記錄

settings.py
LOGGING_CONFIG = None

import logging.config

logging.config.dictConfig(...)

請注意,預設設定程序只會在設定完全載入後呼叫 LOGGING_CONFIG。相反地,在您的設定檔中手動設定日誌記錄會立即載入您的日誌記錄設定。因此,您的日誌記錄設定必須出現在它所依賴的任何設定*之後*。

返回頂端