【一】函式式簡單配置
import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
預設情況下Python的logging模組將日誌列印到了標準輸出中,且只顯示了大於等於WARNING級別的日誌,這說明預設的日誌級別設定為
WARNING(日誌級別等級CRITICAL > ERROR > WARNING > INFO > DEBUG)
預設的日誌格式為日誌級別:Logger名稱:使用者輸出訊息。
【二】靈活配置日誌級別,日誌格式,輸出位置:
import logging
file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8', )
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[file_handler, ],
level=logging.ERROR
)
logging.error('你好')
【三】日誌切割
import time
import logging
from logging import handlers
sh = logging.StreamHandler()
rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024, backupCount=5)
fh = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=5, encoding='utf-8')
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[fh, sh, rh],
level=logging.ERROR
)
for i in range(1, 100000):
time.sleep(1)
logging.error('KeyboardInterrupt error %s' % str(i))
【四】配置引數
- logging.basicConfig()函式中可透過具體引數來更改logging模組預設行為
- 可用引數有:
filename # 用指定的檔名建立FiledHandler,這樣日誌會被儲存在指定的檔案中。
filemode # 檔案開啟方式,在指定了filename時使用這個引數,預設值為“a”還可指定為“w”。
format # 指定handler使用的日誌顯示格式。
datefmt # 指定日期時間格式。
level # 設定rootlogger(後邊會講解具體概念)的日誌級別
stream # 用指定的stream建立StreamHandler。可以指定輸出到sys.stderr,sys.stdout或者檔案(f=open(‘test.log’,’w’)),預設為sys.stderr。若同時列出了filename和stream兩個引數,則stream引數會被忽略。
- format引數中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 數字形式的日誌級別
%(levelname)s 文字形式的日誌級別
%(pathname)s 呼叫日誌輸出函式的模組的完整路徑名,可能沒有
%(filename)s 呼叫日誌輸出函式的模組的檔名
%(module)s 呼叫日誌輸出函式的模組名
%(funcName)s 呼叫日誌輸出函式的函式名
%(lineno)d 呼叫日誌輸出函式的語句所在的程式碼行
%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌資訊時的,自Logger建立以 來的毫秒數
%(asctime)s 字串形式的當前時間。預設格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒
%(thread)d 執行緒ID。可能沒有
%(threadName)s 執行緒名。可能沒有
%(process)d 程序ID。可能沒有
%(message)s使用者輸出的訊息
【五】logger物件配置
import logging
logger = logging.getLogger()
# 建立一個handler,用於寫入日誌檔案
fh = logging.FileHandler('test.log',encoding='utf-8')
# 再建立一個handler,用於輸出到控制檯 ch = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)fh.setFormatter(formatter) ch.setFormatter(formatter)
logger.addHandler(fh)
#logger物件可以新增多個fh和ch物件 logger.addHandler(ch) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')
- logging庫提供了多個元件:
- Logger、Handler、Filter、Formatter。Logger物件提供應用程式可直接使用的介面
- Handler傳送日誌到適當的目的地
- Filter提供了過濾日誌資訊的方法
- Formatter指定日誌顯示格式。
- 另外,可以透過:
- logger.setLevel(logging.Debug)設定級別,
- 當然,也可以透過fh.setLevel(logging.Debug)單對檔案流設定某個級別。
- Logger、Handler、Filter、Formatter。Logger物件提供應用程式可直接使用的介面
【六】日誌詳細使用
【1】logger物件:負責產生日誌
import logging
logger = logging.getLogger('轉賬記錄')
【2】filter物件:負責過濾日誌(直接忽略)
【3】handler物件:負責日誌產生的位置
import logging
# 產生到檔案的
hd1 = logging.FileHandler('a1.log',encoding='utf8')
# 產生到檔案的
hd2 = logging.FileHandler('a2.log',encoding='utf8')
# 產生在終端的
hd3 = logging.StreamHandler()
【4】formatter物件:負責日誌的格式
import logging
fm1 = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
)
fm2 = logging.Formatter(
fmt='%(asctime)s - %(name)s %(message)s',
datefmt='%Y-%m-%d',
)
【5】繫結handler物件
import logging
logger.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)
【6】繫結formatter物件
import logging
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
hd3.setFormatter(fm1)
【7】設定日誌等級
import logging
logger.setLevel(30)
【8】記錄日誌
import logging
logger.debug('寫了半天 好累啊 好熱啊')
【七】配置成字典格式
import logging
import logging.config
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' # 其中name為getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
test_format = '%(asctime)s] %(message)s'
logfile_path = 'a3.log'
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
'test': {
'format': test_format
},
},
'filters': {}, # 過濾日誌
'handlers': {
# 列印到終端的日誌
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 列印到螢幕
'formatter': 'simple'
},
# 列印到檔案的日誌,收集info及以上的日誌
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 儲存到檔案
'formatter': 'standard',
'filename': logfile_path, # 日誌檔案
'maxBytes': 1024 * 1024 * 5, # 日誌大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日誌檔案的編碼,再也不用擔心中文log亂碼了
},
'other': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # 儲存到檔案
'formatter': 'test',
'filename': 'a2.log',
'encoding': 'utf-8',
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置 空字串作為鍵 能夠相容所有的日誌
'': {
'handlers': ['default', 'console'], # 這裡把上面定義的兩個handler都加上,即log資料既寫入檔案又列印到螢幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)傳遞
}, # 當鍵不存在的情況下 (key設為空字串)預設都會使用該k:v配置
'other': {
'handlers': ['other', ],
'level': 'DEBUG',
'propagate': False,
},
},
}
# 使用配置字典
logging.config.dictConfig(LOGGING_DIC) # 自動載入字典中的配置
logger1 = logging.getLogger('xxx')
logger1.debug('好好的 不要浮躁 努力就有收穫')
【八】日誌終極版
import logging
# 一:日誌配置
logging.basicConfig(
# 1、日誌輸出位置:1、終端 2、檔案
# filename='access.log', # 不指定,預設列印到終端
# 2、日誌格式
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
# 3、時間格式
datefmt='%Y-%m-%d %H:%M:%S %p',
# 4、日誌級別
# critical => 50
# error => 40
# warning => 30
# info => 20
# debug => 10
level=30,
)
# 二:輸出日誌
logging.debug('除錯debug')
logging.info('訊息info')
logging.warning('警告warn')
logging.error('錯誤error')
logging.critical('嚴重critical')
'''
# 注意下面的root是預設的日誌名字
WARNING:root:警告warn
ERROR:root:錯誤error
CRITICAL:root:嚴重critical
'''
【九】logging模組處理流程
- logger: 最高層模組,用來輸出log
-
- logger.level來篩選log
- logger.debug()/info()/warning()/error()等輸出log
- handler:
- 經過logger過濾後log會分發給所有handler處理。
- 每個handler有自己的level, formatter, 以及輸出流
-
- handler.level 過濾log
- handler.formatter 決定log輸出的樣式
- StreamHandler:
- 常見的StreamHandler輸出到標準流,FileHandler,輸出到檔案
- level的排序:NOTSET < DEBUG < INFO < WARNING < ERROR
- 只有大於level的才會被處理
- 注意:
- 一條log會經歷兩次過濾
- 一次是logger.level
- 一次是handler.level
- 被前者過濾掉的log不會進入handler處理流程。
- handler的level和logger的level沒有什麼必然關係。
- 一條log會經歷兩次過濾
【十】日誌模板
import logging
import logging.config
import os
import sys
try:
# 想要給日誌上色就安裝這個模組
import coloredlogs
except Exception as e:
if str(e) == "No module named 'coloredlogs'":
pass
# 自定義日誌級別
CONSOLE_LOG_LEVEL = "INFO"
FILE_LOG_LEVEL = "DEBUG"
# 自定義日誌格式
# 列印在檔案裡的格式:時間戳 + 執行緒名 + 執行緒ID + 任務ID + 發出日誌呼叫的原始檔名 + 發出日誌呼叫的原始碼行號 + 日誌級別 + 日誌訊息正文
# [2023-06-04 15:16:05][MainThread:22896][task_id:root][呼叫.py:12][INFO][這是註冊功能]
STANDARD_FORMAT = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]'
# 列印在控制檯的格式:日誌級別 + 時間戳 + 發出日誌呼叫的原始檔名 + 發出日誌呼叫的原始碼行號 + 日誌訊息正文
# [INFO][2023-06-04 15:37:28,019][呼叫.py:12]這是註冊功能
SIMPLE_FORMAT = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
'''
引數詳解:
-1.%(asctime)s: 時間戳,表示記錄時間
-2.%(threadName)s: 執行緒名稱
-3.%(thread)d: 執行緒ID
-4.task_id:%(name)s: 任務ID,即日誌記錄器的名稱
-5.%(filename)s: 發出日誌呼叫的原始檔名
-6.%(lineno)d: 發出日誌呼叫的原始碼行號
-7.%(levelname)s: 日誌級別,如DEBUG、INFO、WARNING、ERROR、CRITICAL等
-8.%(message)s: 日誌訊息正文
'''
# 日誌檔案路徑
# os.getcwd() : 獲取當前工作目錄,即當前python指令碼工作的目錄路徑
# 拼接日誌檔案路徑 : 當前工作路徑 + “logs”(日誌檔案路徑)
LOG_PATH = os.path.join(os.getcwd(), "Activity_logs")
# OS模組 : 建立多層資料夾
# exist_ok=True 的意思是如果該目錄已經存在,則不會丟擲異常。
os.makedirs(LOG_PATH, exist_ok=True)
# log日誌檔案路徑 : 路徑資料夾路徑 + 日誌檔案
LOG_FILE_PATH = os.path.join(LOG_PATH, 'Logs.log')
# 日誌配置字典
LOGGING_DIC = {
# 日誌版本
'version': 1,
# 表示是否要禁用已經存在的日誌記錄器(loggers)。
# 如果設為 False,則已經存在的日誌記錄器將不會被禁用,而是可以繼續使用。
# 如果設為 True,則已經存在的日誌記錄器將會被禁用,不能再被使用。
'disable_existing_loggers': False,
# 格式化程式:用於將日誌記錄轉換為字串以便於處理和儲存。
# 格式化程式定義了每個日誌記錄的輸出格式,並可以包括日期、時間、日誌級別和其他自定義資訊。
'formatters': {
# 自定義格式一:年-月-日 時:分:秒
'standard': {
# 自定義日誌格式 :時間戳 + 執行緒名 + 執行緒ID + 任務ID + 發出日誌呼叫的原始檔名 + 發出日誌呼叫的原始碼行號 + 日誌級別 + 日誌訊息正文
# 這裡要對應全域性的 STANDARD_FORMAT 配置
'format': STANDARD_FORMAT,
# 時間戳格式:年-月-日 時:分:秒
'datefmt': '%Y-%m-%d %H:%M:%S' # 時間戳格式
},
# 自定義格式二:
'simple': {
# 自定義日誌格式:# 日誌級別 + 時間戳 + 發出日誌呼叫的原始檔名 + 發出日誌呼叫的原始碼行號 + 日誌訊息正文
# 這裡要對應全域性的 SIMPLE_FORMAT 配置
'format': SIMPLE_FORMAT
},
},
# 過濾器
'filters': {},
# 日誌處理器
'handlers': {
# 自定義處理器名稱 - 輸出到控制檯螢幕
'console': {
# 設定日誌等級 為INFO
'level': CONSOLE_LOG_LEVEL,
# 表示該處理器將輸出日誌到流(stream):日誌列印到控制檯
'class': 'logging.StreamHandler',
# 日誌列印格式:日誌級別 + 時間戳 + 發出日誌呼叫的原始檔名 + 發出日誌呼叫的原始碼行號 + 日誌訊息正文
# 這裡的配置要對應 formatters 中的 simple 配置
'formatter': 'simple'
},
# 自定義處理器名稱 - 輸出到檔案
'default': {
# 自定義日誌等級
'level': FILE_LOG_LEVEL,
# 標準輸出到檔案
'class': 'logging.handlers.RotatingFileHandler',
# 日誌列印格式:年-月-日 時:分:秒
# 這裡的配置要對應 formatters 中的 standard 配置
'formatter': 'standard',
# 這裡 要注意宣告配置檔案輸出端的檔案路徑
'filename': LOG_FILE_PATH,
# 限制檔案大小:1024 * 1024 * 5 = 5242880,意味著這個變數的值是 5MB(兆位元組)
'maxBytes': 1024 * 1024 * 5,
# 表示保留最近的5個日誌檔案備份。
# 當日志檔案達到最大大小限制時,將會自動輪轉並且保留最新的5個備份檔案,以便檢視先前的日誌記錄。
# 當日志檔案達到最大大小限制時,會自動進行輪轉,後續的檔名將會以數字進行命名,
# 例如,第一個備份檔案將被重新命名為原始日誌檔名加上".1"的字尾,
# 第二個備份檔案將被重新命名為原始日誌檔名加上“.2”的字尾,
# 以此類推,直到保留的備份數量達到設定的最大值。
'backupCount': 5,
# 日誌儲存檔案格式
'encoding': 'utf-8',
},
},
# 日誌記錄器,用於記錄應用程式的執行狀態和錯誤資訊。
'loggers': {
# 空字串作為鍵 能夠相容所有的日誌(當沒有找到對應的日誌記錄器時預設使用此配置)
# 預設日誌配置
'': {
# 日誌處理器 型別:列印到控制檯輸出 + 寫入本地日誌檔案
'handlers': ['default', 'console'],
# 日誌等級 : DEBUG
'level': 'DEBUG',
# 預設情況下,當一個日誌訊息被髮送到一個Logger物件且沒有被處理時,該訊息會被傳遞給它的父Logger物件,以便在更高層次上進行處理。
# 這個傳遞過程稱為“傳播(propagation)”,而propagate引數指定了是否要使日誌訊息向上傳播。
# 將其設定為True表示應該傳播訊息到上一級的Logger物件;如果設定為False則不傳播。
# 表示異常將會在程式中繼續傳播
# 也就是說,如果一個異常在當前的程式碼塊中沒有被處理,它將會在上級程式碼塊或呼叫函式中繼續向上傳遞,直到被某個程式碼塊捕獲或者程式退出。
# 這是 Python 中異常處理機制的預設行為。
# 如果將 'propagate' 設定為 False,則異常不會被傳播,即使在上級程式碼塊中沒有處理異常的語句,程式也會忽略異常並繼續執行。
'propagate': True,
},
},
}
def set_logging_color(name='', ):
# 初始化日誌處理器 - 使用配置字典初始化日誌處理器(將自定義配置載入到日誌處理器中)
# logging.basicConfig(level=logging.WARNING)
logging.config.dictConfig(LOGGING_DIC)
# 例項化日誌處理器物件 - 並賦予日誌處理器等級
logger = logging.getLogger(name)
# 將logger物件傳遞給coloredlogs.install()函式,並執行該函式以安裝彩色日誌記錄器,使日誌資訊在控制檯上呈現為帶有顏色的格式。
# 具體來說,該函式會使用ANSI轉義序列在終端上輸出日誌級別、時間戳和訊息等資訊,並按日誌級別使用不同的顏色來區分它們。這可以讓日誌資訊更易於閱讀和理解。
coloredlogs.install(logger=logger)
# 禁止日誌訊息向更高階別的父記錄器(如果存在)傳遞。
# 通常情況下,當一個記錄器傳送一條日誌訊息時,該訊息會被傳遞給其所有祖先記錄器,直到傳遞到根記錄器為止。但是,透過將logger.propagate設定為False,就可以阻止該記錄器的訊息向上層傳遞。
# 換句話說,該記錄器的訊息只會被髮送到該記錄器的處理程式(或子記錄器)中,而不會傳遞給祖先記錄器,即使祖先記錄器的日誌級別比該記錄器要低。
# 這種方法通常適用於需要對特定記錄器進行控制,並希望完全獨立於其祖先記錄器的情況。
# 確保 coloredlogs 不會將我們的日誌事件傳遞給根 logger,這可以防止我們重複記錄每個事件
logger.propagate = False
# 配置 日誌顏色
# 這段程式碼定義了一個名為coloredFormatter的變數,並將其賦值為coloredlogs.ColoredFormatter。
# 這是一個Python庫中的類,用於建立帶有彩色日誌級別和訊息的格式化器。
# 該變數可以用作日誌記錄器或處理程式的格式化程式,以使日誌輸出更易於閱讀和理解。
coloredFormatter = coloredlogs.ColoredFormatter(
# fmt表示格式字串,它包含了一些佔位符,用於在記錄日誌時動態地填充相關資訊。
# [%(name)s]表示列印日誌時將記錄器的名稱放在方括號內,其中name是一個變數名,將被記錄器名稱所替換。
# %(asctime)s表示列印日誌時將時間戳(格式化為字串)插入到訊息中,其中asctime是時間的字串表示形式。
# %(funcName)s表示列印日誌時將函式名插入到訊息中,其中funcName是函式的名稱。
# %(lineno)-3d表示列印日誌時將行號插入到訊息中,並且將其格式化為3位數字,其中lineno表示行號。
# %(message)s表示列印日誌時將訊息本身插入到訊息中。
# 綜合起來,這個格式字串將在記錄日誌時輸出以下資訊:記錄器名稱、時間戳、函式名稱、行號和日誌訊息。
# 記錄器名稱 + 時間戳 + 函式名稱 + 行號 + 日誌訊息
# [root] 2023-06-04 16:00:57 register 15 this is an info message
fmt='[%(name)s] %(asctime)s %(funcName)s %(lineno)-3d %(message)s',
# 級別顏色字典
level_styles=dict(
# debug 顏色:白色
debug=dict(color='white'),
# info 顏色:藍色
info=dict(color='blue'),
# warning 顏色:黃色 且 高亮
warning=dict(color='yellow', bright=True),
# error 顏色:紅色 且 高亮 且 加粗
error=dict(color='red', bold=True, bright=True),
# critical 顏色:灰色 且 高亮 且 背景色為紅色
critical=dict(color='black', bold=True, background='red'),
),
# 這段程式碼定義了一個名為field_styles的字典 , 其中包含四個鍵值對。
# 每個鍵代表日誌記錄中的不同欄位,而每個值是一個字典,它指定了與該欄位相關聯的樣式選項。
# 具體來說,這些欄位和樣式選項如下:
# name:指定記錄器的名稱,將使用白色顏色。
# asctime:指定日誌記錄的時間戳,將使用白色顏色。
# funcName:指定記錄訊息的函式名稱,將使用白色顏色。
# lineno:指定記錄訊息的原始碼行號,將使用白色顏色。
field_styles=dict(
name=dict(color='white'),
asctime=dict(color='white'),
funcName=dict(color='white'),
lineno=dict(color='white'),
)
)
## 配置 StreamHandler:終端列印介面
# 這行程式碼定義了一個名為ch的日誌處理器。
# 具體來說,它是logging.StreamHandler類的一個例項,用於將日誌輸出到標準輸出流(即控制檯)中。
# 在建立StreamHandler物件時,需要指定要使用的輸出流
# 因此stream=sys.stdout引數指定了該物件將把日誌寫入到標準輸出流中.
ch = logging.StreamHandler(stream=sys.stdout)
# 這段程式碼是 Python 中用於設定日誌輸出格式的語句。
# 它使用了一個名為 coloredFormatter 的格式化器物件,並將其傳遞給 ch.setFormatter() 方法來指定輸出日誌的樣式。
# 具體來說,ch 是一個 Logger 物件,它代表了整個日誌系統中的一個記錄器。
# 可以透過該物件來控制日誌的級別、輸出位置等行為。而 setFormatter() 方法則用於設定該 Logger 輸出日誌的格式化方式
# 這裡傳遞的是 coloredFormatter 格式化器物件。
# 在實際應用中,coloredFormatter 可以是自定義的 Formatter 類或者已經存在的 Formatter 物件。
# 這裡 將 coloredFormatter - 彩色輸出日誌資訊的格式化器 傳入進去。
ch.setFormatter(fmt=coloredFormatter)
# 這段程式碼是在Python中新增一個日誌記錄器(logger)的處理器(handler)。其中,hdlr引數是指定要新增到記錄器(logger)中的處理器(handler)物件,ch是一個代表控制檯輸出的處理器物件。
# 這行程式碼的作用是將控制檯輸出的資訊新增到日誌記錄器中。
logger.addHandler(hdlr=ch)
# 這段程式碼用於設定日誌級別為DEBUG,也就是最低階別的日誌記錄。
# 意思是在程式執行時,只有DEBUG級別及以上的日誌資訊才會被記錄並輸出,而比DEBUG級別更低的日誌資訊則不會被記錄或輸出。
# DEBUG(除錯)、INFO(資訊)、WARNING(警告)、ERROR(錯誤)和CRITICAL(嚴重錯誤)。
logger.setLevel(level=logging.DEBUG)
# 返回日誌生成物件
return logger
def get_logger(name='', ):
'''
:param name: 日誌等級
:return:
'''
# 初始化日誌處理器 - 使用配置字典初始化日誌處理器(將自定義配置載入到日誌處理器中)
logging.config.dictConfig(LOGGING_DIC)
# 例項化日誌處理器物件 - 並賦予日誌處理器等級
logger = logging.getLogger(name)
# 返回日誌生成物件
return logger
if __name__ == "__main__":
# # 示例:
# # (1)初始化日誌處理器 - 使用配置字典初始化日誌處理器(將自定義配置載入到日誌處理器中)
# logging.config.dictConfig(LOGGING_DIC)
# # (2)例項化日誌處理器物件 - 並賦予日誌處理器等級
# # # logger1 = logging.getLogger('') # 預設為 '' 即以預設配置進行例項化
# logger1 = logging.getLogger('')
# # (3)當日志發生時,列印的提示語
# logger1.debug('這是日誌生成語句')
logger_nor = get_logger()
logger_nor.info(msg="this is a debug message")
logger_col = set_logging_color()
logger_col.debug(msg="this is a debug message")