首頁 > 軟體

Django 中使用紀錄檔的方法

2022-07-20 18:02:36

1. 紀錄檔的意義

紀錄檔是個好東西,但卻並不是所有人都願意記,直到出了問題才追悔莫及,長嘆一聲,當初要是記紀錄檔就好了。

但記紀錄檔卻是個技術活,不能什麼都不記,但也不能什麼都記。如果記了很多沒用的資訊,反而給查紀錄檔排錯的過程增加很多困難。

所以,紀錄檔要記錄在程式的關鍵節點,而且內容要簡潔,傳遞資訊要準確。要清楚的反應出程式當時的狀態,時間,錯誤資訊等。

只有做到這樣,我們才能在第一時間找到問題,並且解決問題

開發階段,所有的問題都可以通過偵錯,在程式中輸出,但專案上線後,會進行統一的錯誤處理,不能將錯誤資訊暴漏出來,所以最好的方式,就是將程式執行資訊儲存在紀錄檔中

程式上線後,是萬萬不能沒有紀錄檔的

2. django 中如何處理紀錄檔

Django 使用 Python 內建的 logging 模組處理系統紀錄檔,所以,只要掌握了 Python 中的 logging 模組,基本也就能夠在 django 中使用紀錄檔了

3. Python 中使用紀錄檔

這裡簡單介紹 Python 中 logging 模組的使用,下一章再聊如何在 django 中使用

logging 模組的使用主要包含如下幾個方面

  • 將紀錄檔資訊直接輸出
  • 將紀錄檔資訊儲存到檔案中
  • 輸出變數到紀錄檔中
  • 更改訊息顯示格式
  • 覆蓋紀錄檔
  • 紀錄檔設定

3.1 直接輸出紀錄檔資訊

首先匯入 logging 模組,然後在下面選擇方法進行紀錄檔輸出

級別何時使用
DEBUG細節資訊,僅當診斷問題時適用。
INFO確認程式按預期執行。
WARNING表明有已經或即將發生的意外(例如:磁碟空間不足)。程式仍按預期進行。
ERROR由於嚴重的問題,程式的某些功能已經不能正常執行
CRITICAL嚴重的錯誤,表明程式已不能繼續執行

簡單例子

logging.debug('出現了bug')
logging.info('一般資訊')
logging.warning('警告資訊以下級別預設不出現')
logging.error('出現了錯誤')
logging.critical('嚴重問題')

紀錄檔資訊,被直接輸出出來,並沒有記錄到紀錄檔中

3.2 設定紀錄檔級別

上面的 logging.info('一般資訊')logging.debug('出現了bug') 並沒有任何輸出,原因在於 logging 模組預設只輸出 WARNING以上級別(包含 WARNING)

通過 basicConfig 方法更改紀錄檔級別

# 更改紀錄檔級別
    logging.basicConfig(level=logging.INFO)
    logging.info('一般資訊')
    logging.debug('出現了bug')
    logging.warning('警告資訊以下級別預設不出現')
    logging.error('出現了錯誤')
    logging.critical('嚴重問題')

級別 INFO 高於 DEBUG,如果希望 logging.debug 方法生效,需要更改 level 為 logging.DEBUG

3.3 儲存紀錄檔到檔案

實際開發中,紀錄檔資訊一定要儲存到檔案的

basicConfig 方法頁可以設定紀錄檔檔案的目錄資訊

修改案例中第1行程式碼,新增 filename 引數,設定紀錄檔檔案目錄和名稱

logging.basicConfig(filename='0707.log', level=logging.INFO)

開啟紀錄檔檔案,發現亂碼了

3.4 設定編碼

basicConfig 方法頁可以設定紀錄檔檔案編碼

logging.basicConfig(filename='0707.log', encoding='utf-8', level=logging.INFO)

3.5 覆蓋紀錄檔檔案

預設情況下,新的紀錄檔內容採用的是追加模式

可以通過 filemode 引數設定覆蓋之前的紀錄檔內容

logging.basicConfig(filename='0707.log', filemode='w', encoding='utf-8', level=logging.INFO)

basicConfig()被設計為一次性的設定,只有第一次呼叫會進行操作,隨後的呼叫不會產生有效操作

此段的意思是,當程式啟動後,第一次呼叫上面的方法,會生效,後面如果程式沒有重新啟動,無論呼叫多少次,此程式碼都不會生效

看下面程式碼

logging.basicConfig(filename='0707.log', filemode='w', encoding='utf-8', level=logging.INFO)
logging.info('一般資訊')

啟動程式,或者修改程式碼儲存時也會熱過載,此時紀錄檔檔案內容就會被覆蓋

但在沒有重啟的情況下,無論上面程式碼執行多少次,都不會覆蓋內容,而是追加

此種模式的意義在於:程式重啟後,舊的紀錄檔對於我們沒有意義的情況

3.6 記錄變數到紀錄檔

可以使用下面兩種方式進行變數的格式化

logging.basicConfig(filename='0707.log', filemode='w', encoding='utf-8', level=logging.INFO)
logging.info('採用 %s 的方式輸出變數', '%s')
logging.info('採用{}的方式輸出變數'.format('format'))

3.7 更改顯示訊息的組成

這是預設情況下紀錄檔訊息的組成

如果想更改,可以通過 basicConfig 方法的 format 引數設定

logging.basicConfig(format='%(levelname)s:%(message)s', filename='0707.log', filemode='w', encoding='utf-8',
                        level=logging.INFO)
    logging.info('採用 %s 的方式輸出變數', '%s')
    logging.info('採用{}的方式輸出變數'.format('format'))

下面只顯示級別和紀錄檔內容,沒有 root

程式碼中levelnamemessage LogRecord 的屬性,完整屬性列表如下

args此屬性不需要使用者進行格式化。合併到 msg 以產生 message 的包含引數的元組,或是其中的值將被用於合併的字典(當只有一個引數且其型別為字典時)。
asctime%(asctime)s表示人類易讀的 LogRecord 生成時間。 預設形式為 ‘2003-07-08 16:49:45,896’ (逗號之後的數位為時間的毫秒部分)。
created%(created)fLogRecord 被建立的時間(即 time.time() 的返回值)。
exc_info此屬性不需要使用者進行格式化。異常元組(例如 sys.exc_info)或者如未發生異常則為 None
filename%(filename)spathname 的檔名部分。
funcName%(funcName)s函數名包括呼叫紀錄檔記錄.
levelname%(levelname)s訊息文字記錄級別('DEBUG''INFO''WARNING''ERROR''CRITICAL')。
levelno%(levelno)s訊息數位的記錄級別 (DEBUG, INFO, WARNING, ERROR, CRITICAL).
lineno%(lineno)d發出紀錄檔記錄呼叫所在的源行號(如果可用)。
message%(message)s記入紀錄檔的訊息,即 msg % args 的結果。 這是在發起呼叫 Formatter.format() 時設定的。
module%(module)s模組 (filename 的名稱部分)。
msecs%(msecs)dLogRecord 被建立的時間的毫秒部分。
msg此屬性不需要使用者進行格式化。在原始紀錄檔記錄呼叫中傳入的格式字串。 與 args 合併以產生 message,或是一個任意物件 (參見 使用任意物件作為訊息)。
name%(name)s用於記錄呼叫的紀錄檔記錄器名稱。
pathname%(pathname)s發出紀錄檔記錄呼叫的原始檔的完整路徑名(如果可用)。
process%(process)d程序ID(如果可用)
processName%(processName)s程序名(如果可用)
relativeCreated%(relativeCreated)d以毫秒數表示的 LogRecord 被建立的時間,即相對於 logging 模組被載入時間的差值。
stack_info此屬性不需要使用者進行格式化。當前執行緒中從堆疊底部起向上直到包括紀錄檔記錄呼叫並引發建立當前記錄堆疊幀建立的堆疊幀資訊(如果可用)。
thread%(thread)d執行緒ID(如果可用)
threadName%(threadName)s執行緒名(如果可用)

比如,修改上面程式碼,加上 asctime 屬性

logging.basicConfig(format='%(levelname)s:%(message)s:%(asctime)s', filename='0707.log', filemode='w', encoding='utf-8',
                        level=logging.INFO)

檢視紀錄檔

3.8 模組化

上面介紹的方法已經可以為程式設定紀錄檔功能了

logging 模組頁提供了模組化的方法,通過下面幾個元件來設定紀錄檔

  • 記錄器:暴露了應用程式程式碼直接使用的介面
  • 處理器:將紀錄檔記錄(由記錄器建立)傳送到適當的目標
  • 過濾器:提供了更細粒度的功能,用於確定要輸出的紀錄檔記錄
  • 格式器:指定最終輸出中紀錄檔記錄的樣式

其實完成的還是上面的功能,只不過可以進行模組化拆分,比如可以建立多個處理器,多個格式器,通過設定的方式進行處理器、格式器的切換

3.8.1 通過 Python 方法設定

# 建立記錄器
logger = logging.getLogger('simple')
# 設定紀錄檔記錄級別
logger.setLevel(logging.DEBUG)
# 建立處理器
ch = logging.StreamHandler()
# 設定處理器級別
ch.setLevel(logging.DEBUG)
# 建立格式器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 為處理器設定格式器
ch.setFormatter(formatter)
# 將處理器新增到記錄器
logger.addHandler(ch)

logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

總結:

①:建立記錄器:記錄器中提供了 info、debu、warning、error、critical 方法用來記錄紀錄檔

②:設定記錄器級別:此級別了哪個級別以上的資訊會被記錄到紀錄檔中

③:建立處理器:處理器決定如何處理訊息、比如列印到控制檯、寫入紀錄檔檔案、傳送郵件等

④:設定處理器級別:決定處理器傳送哪些訊息。記錄器中設定的級別,決定哪個級別以上的訊息會被傳送給處理器,處理器中的設定級別決定了,哪些訊息會被處理,比如哪些訊息會被寫到檔案中。

⑤:建立格式器:決定紀錄檔的格式和紀錄檔中包含哪些內容

⑥:將格式器新增到處理器:處理器將使用此格式器格式化紀錄檔

⑦:將處理器新增到記錄器:記錄器將使用此處理器處理紀錄檔

如果希望紀錄檔儲存到檔案中,只需重新建立一個處理器,將其新增到記錄器中即可

# 建立控制器,將紀錄檔寫入到檔案中
    ch_file = logging.FileHandler('aa.log')
    ch_file.setLevel(logging.DEBUG)
    # 為處理器設定格式器
    ch_file.setFormatter(formatter)

    # 將處理器新增到記錄器
    logger.addHandler(ch_file)

3.8.2 設定字典

Python 3.2中引入的一種新的設定紀錄檔記錄的方法–用字典來儲存logging設定資訊。這相對於上面所講的基於組態檔來儲存logging設定資訊的方式來說,功能更加強大,也更加靈活,因為我們可把很多的資料轉換成字典。比如,我們可以使用JSON格式的組態檔、YAML格式的組態檔,然後將它們填充到一個設定字典中;或者,我們也可以用Python程式碼構建這個設定字典,或者通過socket接收pickled序列化後的設定資訊。總之,你可以使用你的應用程式可以操作的任何方法來構建這個設定字典

編寫設定字典,然後通過 logging.config.dictConfig 方法呼叫此字典

1、簡單設定

    # 用於格式化
    simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

    log_dict = {
        "version": 1,
        'disable_existing_loggers': False,
        "formatters": {
            "simple": {
                'format': simple_format
            }
        },

        "handlers": {
            "console": {
                "class": 'logging.StreamHandler',
                "level": 'INFO',
                "formatter": "simple"
            }
        },
        "loggers": {
            "simple": {
                "level": "DEBUG",
                "handlers": ['console'],
                'propagate': True,
            }
        }
    }
    logging.config.dictConfig(log_dict)

    logger = logging.getLogger('simple')

    logger.debug('debug message')
    logger.info('info message')
    logger.warning('warn message')
    logger.error('error message')
    logger.critical('critical message')

①:建立了一個名為 simple 的格式器

②:建立了一個名為 console 的處理器,並設定處理器使用的格式器為 simple

③:建立了額一個名為 simple 的記錄器,並設定記錄器使用的處理器為 console

2、增加新的格式器、處理器和記錄器

建立一個新的格式器

standard = '%(levelname)s:%(asctime)s:%(filename)s:%(lineno)d:%(message)s'

新增

建立新的處理器,用於將紀錄檔寫入檔案

在記錄器中新增此處理器

執行程式,紀錄檔將同時列印到控制檯和紀錄檔檔案中

這裡應該體會到了使用設定字典的好處

建立記錄器時,選擇 standard,則只會輸出 ERROR 以上的紀錄檔資訊

3、組態檔-yaml

開發時,最好的方式,當然不是在程式中編寫程式碼做出上面設定

而是,新建組態檔,程式執行時,讀取組態檔

我們將設定寫到一個 yaml ,然後讀取此檔案

根目錄下新建 log_config.yml (yaml 檔案的字尾名可以是yaml 或者 yml)

version: 1
formatters:
    simple:
        format: '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
    standard:
        format: '%(levelname)s:%(asctime)s:%(filename)s:%(lineno)d:%(message)s'
handlers:
    console:
        class: logging.StreamHandler
        level: DEBUG
        formatter: simple
    file:
        class: logging.FileHandler
        formatter: simple
        filename: logtest.log

loggers:
    simple:
        level: DEBUG
        handlers: [ file ]
        propagate: True
    standard:
        level: ERROR
        handlers: [ console,file ]
        propagate: True

檢視函數程式碼,讀取 yaml 檔案,並將其轉換成 dict,使用 logging.config.dictConfig 方法載入設定,並使用其中名稱為 simple 的記錄器

BASE_DIR = Path(__file__).resolve().parent.parent
    with open(str(BASE_DIR) + 'config.yaml', 'r', encoding='utf-8') as f:
        file = f.read()
        config = yaml.load(file, Loader=yaml.FullLoader)

    logging.config.dictConfig(config)

    logger = logging.getLogger('simple')

    logger.debug('debug message')
    logger.info('info message')
    logger.warning('warn message')
    logger.error('error message')
    logger.critical('critical message')

切換記錄器,看看效果

練習:自己再設定幾個處理器和格式器

4、寫到 settings 中

也可以將上面的設定直接寫到 settings.py 中

變數名稱必須為 LOGGING

# 紀錄檔設定
LOGGING = {
    "version": 1,
    "formatters": {
        "simple": {
            "format": '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
        },
        "standard": {
            "format": '%(levelname)s:%(asctime)s:%(filename)s:%(lineno)d:%(message)s'
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "DEBUG",
            "formatter": "simple"
        },
        "file": {
            "class": "logging.FileHandler",
            "formatter": "simple",
            "filename": "logtest.log"
        }
    },
    "loggers": {
        "simple": {
            "level": "DEBUG",
            "handlers": ["file"],
            "propagate": True
        },
        "standard": {
            "level": "ERROR",
            "handlers": ["console", "file"],
            "propagate": True
        }
    }
}

檢視函數

logger = logging.getLogger('simple')

logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

4. django 紀錄檔

上面介紹了 Python 中使用紀錄檔的方式,有了這些基礎之後,學習 django 中紀錄檔的使用就事半功倍了

django 中仍然使用了 logging 模組做紀錄檔處理

其實,3.8 節已經講完了,就是這麼用。。。。

附:關於 logging 模組的詳細說明

在Python的logging模組中,主要包含下面四大方面:

  • Loggers: 記錄器
  • Handlers:處理器
  • Filters: 過濾器
  • Formatters: 格式化器

下文詳細說明一下這四大模組

4.1 Loggers

logger 是紀錄檔系統的入口。每個 logger 都是命名了的 bucket, 訊息寫入 bucket 以便進一步處理。

logger 可以設定紀錄檔級別。紀錄檔級別描述了由該 logger 處理的訊息的嚴重性。Python 定義了下面幾種紀錄檔級別:

DEBUG:排查故障時使用的低階別系統資訊;INFO:一般的系統資訊;WARNING:描述系統發生了一些小問題的資訊;ERROR:描述系統發生了大問題的資訊;CRITICAL:描述系統發生嚴重問題的資訊;

每一條寫入 logger 的訊息都是一條紀錄檔記錄。每一條紀錄檔記錄也包含紀錄檔級別,代表對應訊息的嚴重程度。紀錄檔記錄還包含有用的後設資料,來描述被記錄的事件細節,例如堆疊跟蹤或者錯誤碼。

當logger處理一條訊息時,會將自己的紀錄檔級別和這條訊息的紀錄檔級別做對比。如果訊息的紀錄檔級別匹配或者高於 logger 的紀錄檔級別,它就會被進一步處理。否則這條訊息就會被忽略掉。

當 logger 確定了一條訊息需要處理之後,會把它傳給Handler。

4.2 Handlers

Handler是決定如何處理logger中每一條訊息的引擎。它描述特定的紀錄檔行為,比如把訊息輸出到螢幕、檔案或網路 socket。

和logger一樣,handler也有紀錄檔級別的概念。如果一條紀錄檔記錄的級別不匹配或者低於handler的紀錄檔級別,對應的訊息會被 handler忽略。

一個logger可以有多個handler,每一個handler可以有不同的紀錄檔級別。這樣就可以根據訊息的重要性不同,來提供不同格式的輸出。例如,你可以新增一個 handler 把ERROR和CRITICAL訊息發到尋呼機,再新增另一個handler把所有的訊息(包括 ERROR和CRITICAL訊息)儲存到檔案裡以便日後分析。

4.3 過濾器

在紀錄檔從 logger 傳到 handler 的過程中,使用 Filter 來做額外的控制。

預設情況下,只要級別匹配,任何紀錄檔訊息都會被處理。不過,也可以通過新增filter來給紀錄檔處理的過程增加額外條件。例如,可以新增一個filter只允許某個特定來源的ERROR訊息輸出。

Filter還被用來在紀錄檔輸出之前對紀錄檔記錄做修改。例如,可以寫一個filter,當滿足一定條件時,把紀錄檔記錄從ERROR降到 WARNING級別。

Filter在logger和handler中都可以新增;多個filter可以連結起來使用,來做多重過濾操作。

4.4 Formatters

紀錄檔記錄最終是需要以文字來呈現的。Formatter 描述了文字的格式。一個 formatter 通常由包含 LogRecord attributes 的 Python 格式化字串組成,不過你也可以為特定的格式來設定自定義的 formatter。

到此這篇關於django 中使用紀錄檔的文章就介紹到這了,更多相關django 使用紀錄檔內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com