Python logging模塊詳細(xì)教程與最佳實(shí)踐
Python logging模塊完整教程
Python的logging模塊是標(biāo)準(zhǔn)庫中強(qiáng)大而靈活的日志記錄工具,它能夠幫助開發(fā)者記錄應(yīng)用程序運(yùn)行過程中的各種事件和信息。通過本教程,您將從基礎(chǔ)概念開始,逐步學(xué)習(xí)如何配置、使用和優(yōu)化日志系統(tǒng),最終掌握在實(shí)際項(xiàng)目中應(yīng)用logging模塊的各種技巧。本教程涵蓋了從簡單的日志輸出到高級的日志配置和性能優(yōu)化的完整內(nèi)容。
什么是 Python logging
Python logging模塊是Python標(biāo)準(zhǔn)庫的一部分,提供了一個靈活的框架來記錄應(yīng)用程序運(yùn)行時的各種事件和信息。
核心特點(diǎn)
- 層次化日志系統(tǒng): 支持多個logger形成層次結(jié)構(gòu)
- 多種日志級別: DEBUG、INFO、WARNING、ERROR、CRITICAL
- 靈活的輸出目標(biāo): 控制臺、文件、網(wǎng)絡(luò)等
- 可配置的格式: 自定義日志消息格式
- 線程安全: 支持多線程環(huán)境
- 性能優(yōu)化: 支持異步日志處理
快速開始
學(xué)習(xí)建議
建議先跟著這個例子動手實(shí)踐,理解基本流程后再深入學(xué)習(xí)細(xì)節(jié)。
Hello World 示例
import logging
# 基本的日志記錄
logging.debug("這是一條調(diào)試信息")
logging.info("這是一條信息")
logging.warning("這是一條警告")
logging.error("這是一條錯誤")
logging.critical("這是一條嚴(yán)重錯誤")代碼說明:
import logging: 導(dǎo)入logging模塊logging.debug(): 記錄調(diào)試級別信息logging.info(): 記錄普通信息logging.warning(): 記錄警告信息logging.error(): 記錄錯誤信息logging.critical(): 記錄嚴(yán)重錯誤信息
運(yùn)行結(jié)果:
WARNING:root:這是一條警告
ERROR:root:這是一條錯誤
CRITICAL:root:這是一條嚴(yán)重錯誤
代碼解析
- 默認(rèn)配置: 默認(rèn)情況下,只有WARNING及以上級別的日志會被顯示
- 默認(rèn)格式:
[級別]:[logger名稱]:[消息] - 默認(rèn)輸出: 默認(rèn)輸出到stderr(標(biāo)準(zhǔn)錯誤流)
[!question] 為什么要這樣寫?
logging模塊的默認(rèn)配置相對保守,只顯示W(wǎng)ARNING級別的日志。這是因?yàn)檫^多的日志信息可能會干擾程序的主要輸出,同時日志系統(tǒng)的開銷也應(yīng)該被控制。
[!bug] 如果沒有看到DEBUG和INFO日志
這是正常的!logging默認(rèn)只顯示W(wǎng)ARNING及以上級別的日志。要顯示所有級別的日志,需要配置logging級別。
核心概念
概念1:Logger(記錄器)
定義: Logger是日志系統(tǒng)的核心組件,用于創(chuàng)建和記錄日志消息。
為什么重要: Logger為應(yīng)用程序提供了記錄日志的入口點(diǎn),它決定了哪些級別的消息會被記錄,以及消息應(yīng)該發(fā)送到哪里。
工作原理:
Logger創(chuàng)建后會被分配一個名稱和日志級別。當(dāng)調(diào)用logger的日志方法時,會檢查消息的級別是否高于或等于logger的級別,如果是則處理該消息。
示例:
import logging
# 創(chuàng)建logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 記錄日志
logger.debug("調(diào)試消息")
logger.info("信息消息")
logger.warning("警告消息")[!note] 深入理解
- Logger名稱通常采用點(diǎn)分格式,如’my_app.database’
- 同一個名稱的logger返回的是同一個實(shí)例
- 如果不設(shè)置級別,會繼承父logger的級別
概念2:Handler(處理器)
定義: Handler負(fù)責(zé)將日志消息發(fā)送到目標(biāo)位置,如控制臺、文件、網(wǎng)絡(luò)等。
為什么重要: Handler決定了日志的最終去向,是實(shí)現(xiàn)日志系統(tǒng)靈活性的關(guān)鍵。
工作原理:
每個Logger可以附加多個Handler。當(dāng)Logger記錄消息時,會將消息傳遞給所有附加的Handler,每個Handler根據(jù)其配置獨(dú)立處理消息。
示例:
import logging
# 創(chuàng)建logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 控制臺handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 文件handler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
# 添加handler
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 記錄日志
logger.debug("只記錄到文件")
logger.info("同時記錄到控制臺和文件")[!note] 深入理解
- 一個Logger可以關(guān)聯(lián)多個Handler
- 每個Handler可以有自己的日志級別和格式
- 可以通過Handler實(shí)現(xiàn)日志過濾、分發(fā)等功能
概念3:Formatter(格式化器)
定義: Formatter定義了日志消息的輸出格式。
為什么重要: 好的日志格式能夠使日志信息更加清晰、易讀,便于問題排查。
工作原理:
Formatter將日志事件對象轉(zhuǎn)換為字符串??梢宰远x包含時間戳、日志級別、logger名稱、消息等字段。
示例:
import logging
# 創(chuàng)建formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 創(chuàng)建handler并設(shè)置formatter
handler = logging.StreamHandler()
handler.setFormatter(formatter)
# 使用handler
logger = logging.getLogger('my_app')
logger.addHandler(handler)
# 記錄日志
logger.info("格式化的日志消息")輸出:
2024-01-01 12:00:00,123 - my_app - INFO - 格式化的日志消息
[!note] 深入理解
%(asctime)s: 時間戳%(name)s: logger名稱%(levelname)s: 日志級別%(message)s: 日志消息- 可以添加更多字段,如文件名、行號、函數(shù)名等
概念之間的關(guān)系


Logger作為入口點(diǎn),通過日志級別控制哪些消息會被記錄。Logger可以關(guān)聯(lián)多個Handler,每個Handler可以有自己的Formatter來格式化輸出。這樣實(shí)現(xiàn)了日志系統(tǒng)的靈活配置。
基礎(chǔ)功能詳解
功能1:日志級別
用途: 控制哪些信息應(yīng)該被記錄,幫助過濾日志信息。
基本語法:
logging.debug("消息") # 調(diào)試級別
logging.info("消息") # 信息級別
logging.warning("消息") # 警告級別
logging.error("消息") # 錯誤級別
logging.critical("消息") # 嚴(yán)重錯誤級別參數(shù)說明:
message: 日志消息字符串,可以包含格式化占位符
簡單示例:
import logging
# 設(shè)置日志級別
logging.basicConfig(level=logging.DEBUG)
logging.debug("調(diào)試信息:程序開始執(zhí)行")
logging.info("信息:加載配置文件")
logging.warning("警告:配置文件版本可能過時")
logging.error("錯誤:無法連接到數(shù)據(jù)庫")
logging.critical("嚴(yán)重錯誤:系統(tǒng)即將崩潰")常見用法:
import logging
# 獲取logger
logger = logging.getLogger('app')
# 設(shè)置日志級別
logger.setLevel(logging.INFO) # 只記錄INFO及以上級別
# 針對不同級別有不同處理
if some_critical_condition:
logger.critical("關(guān)鍵問題發(fā)生,需要立即處理")
elif some_error_condition:
logger.error("發(fā)生錯誤,影響功能正常運(yùn)行")
else:
logger.info("系統(tǒng)運(yùn)行正常")[!tip] 最佳實(shí)踐
- 在開發(fā)環(huán)境中使用DEBUG級別記錄詳細(xì)信息
- 在生產(chǎn)環(huán)境中使用INFO或WARNING級別避免日志過多
- 根據(jù)重要性選擇合適的日志級別
[!warning] 常見陷阱
- 不要過度使用DEBUG級別,否則會產(chǎn)生大量無用日志
- 避免在循環(huán)中頻繁記錄INFO級別日志,影響性能
- 確保錯誤信息包含足夠的上下文信息
功能2:基本配置
用途: 快速配置logging系統(tǒng)的基本參數(shù)。
基本語法:
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='app.log',
filemode='a'
)
參數(shù)說明:
level: 日志級別閾值format: 日志消息格式filename: 日志文件名filemode: 文件打開模式('w’覆蓋,'a’追加)datefmt: 日期時間格式style: 格式樣式(‘%’, ‘{’, ‘$’)force: 是否重新配置已經(jīng)存在的logging
簡單示例:
import logging
# 基本配置
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='my_app.log',
filemode='a'
)
# 現(xiàn)在所有日志都會寫入文件
logging.debug("調(diào)試信息")
logging.info("信息")常見用法:
import logging
# 生產(chǎn)環(huán)境配置
logging.basicConfig(
level=logging.WARNING,
format='%(asctime)s [%(levelname)s] %(message)s',
filename='production.log'
)
# 開發(fā)環(huán)境配置
import sys
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(name)s %(levelname)s: %(message)s',
stream=sys.stdout # 輸出到控制臺
)[!tip] 最佳實(shí)踐
- 在程序入口處進(jìn)行一次basicConfig配置
- 根據(jù)環(huán)境(開發(fā)/測試/生產(chǎn))調(diào)整日志級別
- 使用不同的輸出目標(biāo)(控制臺/文件)進(jìn)行調(diào)試
[[文件操作]]
功能3:Logger創(chuàng)建和使用
用途: 創(chuàng)建和管理應(yīng)用程序的logger實(shí)例。
基本語法:
logger = logging.getLogger('logger_name')
logger.debug("消息")
參數(shù)說明:
logger_name: logger的名稱,建議使用模塊名或類名
簡單示例:
import logging
# 創(chuàng)建不同模塊的logger
db_logger = logging.getLogger('my_app.database')
web_logger = logging.getLogger('my_app.web')
user_logger = logging.getLogger('my_app.user')
# 配置logger級別
db_logger.setLevel(logging.DEBUG)
web_logger.setLevel(logging.INFO)
user_logger.setLevel(logging.WARNING)
# 記錄日志
db_logger.debug("數(shù)據(jù)庫連接建立")
db_logger.info("執(zhí)行SQL查詢")
web_logger.info("收到HTTP請求")
user_logger.warning("用戶嘗試訪問無權(quán)限的頁面")常見用法:
import logging
import os
# 基于模塊名創(chuàng)建logger
logger = logging.getLogger(__name__)
# 如果logger還沒有配置,進(jìn)行基本配置
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# 使用logger
logger.debug("Debug message")
logger.info("App started")
logger.error("Failed to process request")[!tip] 最佳實(shí)踐
- 使用
__name__作為logger名稱,這樣能清楚地知道日志來源 - 每個模塊創(chuàng)建自己的logger實(shí)例
- 避免直接使用logging模塊的函數(shù),而是使用logger實(shí)例
功能4:Handler配置
用途: 配置日志輸出的目標(biāo)位置和方式。
基本語法:
# StreamHandler - 輸出到控制臺
console_handler = logging.StreamHandler()
# FileHandler - 輸出到文件
file_handler = logging.FileHandler('app.log')
# RotatingFileHandler - 輪轉(zhuǎn)文件
from logging.handlers import RotatingFileHandler
rotating_handler = RotatingFileHandler(
'app.log',
maxBytes=1024*1024, # 1MB
backupCount=5
)
# TimedRotatingFileHandler - 按時間輪轉(zhuǎn)
from logging.handlers import TimedRotatingFileHandler
timed_handler = TimedRotatingFileHandler(
'app.log',
when='midnight',
backupCount=7
)參數(shù)說明:
StreamHandler: 流處理器,可以指定輸出流(默認(rèn)是stderr)FileHandler: 文件處理器maxBytes: 文件最大大?。≧otatingFileHandler)backupCount: 保留的備份文件數(shù)when: 輪轉(zhuǎn)時間間隔(TimedRotatingFileHandler)
簡單示例:
import logging
from logging.handlers import RotatingFileHandler
# 創(chuàng)建logger
logger = logging.getLogger('app')
logger.setLevel(logging.DEBUG)
# 控制臺handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 文件handler
file_handler = RotatingFileHandler(
'app.log',
maxBytes=1024*1024, # 1MB
backupCount=3,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
# 添加handler
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 測試日志
logger.debug("這是調(diào)試信息,只記錄到文件")
logger.info("這是信息,同時記錄到控制臺和文件")常見用法:
import logging
import sys
from logging.handlers import SMTPHandler
# 配置多輸出
logger = logging.getLogger('my_app')
# 控制臺輸出
console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.INFO)
console_format = logging.Formatter('%(levelname)s: %(message)s')
console.setFormatter(console_format)
# 文件輸出
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.DEBUG)
file_format = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(file_format)
# 郵件handler(僅用于嚴(yán)重錯誤)
mail_handler = SMTPHandler(
mailhost=('smtp.example.com', 587),
fromaddr='app@example.com',
toaddrs=['admin@example.com'],
subject='Application Error',
credentials=('username', 'password'),
secure=()
)
mail_handler.setLevel(logging.CRITICAL)
# 添加handler
logger.addHandler(console)
logger.addHandler(file_handler)
logger.addHandler(mail_handler)[!tip] 最佳實(shí)踐
- 為不同的日志級別使用不同的輸出渠道
- 生產(chǎn)環(huán)境中配置適當(dāng)?shù)妮嗈D(zhuǎn)策略,避免日志文件過大
- 使用不同的格式滿足不同的輸出需求
[[異常處理]]
[[Python基礎(chǔ)語法]]
實(shí)踐項(xiàng)目
[!example] 項(xiàng)目目標(biāo)
通過構(gòu)建一個簡單的Web服務(wù)器日志系統(tǒng),綜合運(yùn)用前面學(xué)到的logging知識。
項(xiàng)目需求
- 記錄HTTP請求信息
- 根據(jù)請求狀態(tài)分類日志級別
- 實(shí)現(xiàn)日志輪轉(zhuǎn)和歸檔
- 支持多種輸出格式
項(xiàng)目結(jié)構(gòu)
web_logger/
├── main.py
├── logger_config.py
├── requirements.txt
└── logs/
└── app.log
實(shí)現(xiàn)步驟
步驟1:創(chuàng)建日志配置模塊
目標(biāo):創(chuàng)建一個可復(fù)用的日志配置模塊。
代碼:
# logger_config.py
import logging
import logging.handlers
import os
from datetime import datetime
class WebLoggerConfig:
def __init__(self, log_dir='logs'):
self.log_dir = log_dir
self.logger = logging.getLogger('web_server')
self._setup_logger()
def _setup_logger(self):
# 確保日志目錄存在
os.makedirs(self.log_dir, exist_ok=True)
# 設(shè)置logger級別
self.logger.setLevel(logging.DEBUG)
# 清除現(xiàn)有的handler
self.logger.handlers.clear()
# 創(chuàng)建formatter
detailed_formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
# 控制臺handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(detailed_formatter)
# 文件handler - 主日志
file_handler = logging.handlers.RotatingFileHandler(
os.path.join(self.log_dir, 'app.log'),
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(detailed_formatter)
# 錯誤日志handler
error_handler = logging.handlers.RotatingFileHandler(
os.path.join(self.log_dir, 'error.log'),
maxBytes=5*1024*1024, # 5MB
backupCount=3,
encoding='utf-8'
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(detailed_formatter)
# 添加所有handler
self.logger.addHandler(console_handler)
self.logger.addHandler(file_handler)
self.logger.addHandler(error_handler)
def get_logger(self):
return self.logger解釋:
- 創(chuàng)建了WebLoggerConfig類來管理日志配置
- 實(shí)現(xiàn)了多級日志輸出(控制臺、主文件、錯誤文件)
- 使用RotatingFileHandler實(shí)現(xiàn)日志輪轉(zhuǎn)
- 錯誤日志單獨(dú)記錄,便于問題排查
步驟2:創(chuàng)建主應(yīng)用程序
目標(biāo):模擬Web服務(wù)器處理HTTP請求并記錄日志。
代碼:
# main.py
from logger_config import WebLoggerConfig
import random
import time
from datetime import datetime
# 初始化日志系統(tǒng)
logger_config = WebLoggerConfig()
logger = logger_config.get_logger()
def simulate_web_request(request_id):
"""模擬處理HTTP請求"""
logger.info(f"收到請求 #{request_id}")
# 模擬請求處理時間
process_time = random.uniform(0.1, 2.0)
time.sleep(process_time)
# 模擬不同的響應(yīng)狀態(tài)
status_code = random.choices(
[200, 404, 500],
weights=[0.8, 0.15, 0.05]
)[0]
# 記錄處理結(jié)果
if status_code == 200:
logger.debug(f"請求 #{request_id} 處理成功,耗時: {process_time:.2f}s")
logger.info(f"響應(yīng) #{request_id}: {status_code} OK")
return "OK"
elif status_code == 404:
logger.warning(f"請求 #{request_id}: 404 Not Found - 資源不存在")
return "Not Found"
else:
logger.error(f"請求 #{request_id} 處理失敗,錯誤代碼: {status_code}")
logger.error(f"詳細(xì)信息: 服務(wù)器內(nèi)部錯誤")
return "Internal Server Error"
def main():
"""主程序"""
logger.info("Web服務(wù)器啟動")
logger.info("開始處理請求...")
try:
# 模擬處理100個請求
for i in range(1, 101):
result = simulate_web_request(i)
# 每10個請求輸出統(tǒng)計信息
if i % 10 == 0:
logger.info(f"已處理 {i} 個請求")
except KeyboardInterrupt:
logger.warning("收到中斷信號,正在關(guān)閉服務(wù)器...")
except Exception as e:
logger.critical(f"服務(wù)器發(fā)生嚴(yán)重錯誤: {str(e)}", exc_info=True)
finally:
logger.info("Web服務(wù)器關(guān)閉")
if __name__ == "__main__":
main()解釋:
- 使用WebLoggerConfig獲取配置好的logger
- 實(shí)現(xiàn)了模擬HTTP請求處理的函數(shù)
- 根據(jù)響應(yīng)狀態(tài)碼使用不同的日志級別
- 添加了異常處理和程序生命周期日志
完整代碼
項(xiàng)目的完整代碼包含兩個文件:
logger_config.py- 日志配置模塊main.py- 主應(yīng)用程序
運(yùn)行和測試
# 安裝依賴(這個項(xiàng)目不需要額外依賴) pip install -r requirements.txt # 運(yùn)行程序 python main.py
預(yù)期輸出:
2024-01-01 10:00:00,123 [INFO] web_server: Web服務(wù)器啟動
2024-01-01 10:00:00,124 [INFO] web_server: 開始處理請求...
2024-01-01 10:00:00,125 [INFO] web_server: 收到請求 #1
2024-01-01 10:00:01,321 [INFO] web_server: 響應(yīng) #1: 200 OK
2024-01-01 10:00:01,322 [INFO] web_server: 收到請求 #2
2024-01-01 10:00:02,456 [WARNING] web_server: 請求 #2: 404 Not Found - 資源不存在
...
2024-01-01 10:00:15,789 [INFO] web_server: 已處理 10 個請求
...
檢查logs目錄下的日志文件,可以看到:
app.log- 包含所有日志error.log- 只包含ERROR級別的日志
進(jìn)階主題
主題1:自定義日志級別
什么時候需要: 當(dāng)內(nèi)置的日志級別不足以滿足業(yè)務(wù)需求時,如需要區(qū)分不同類型的警告。
深入理解:
Python logging允許創(chuàng)建自定義的日志級別,這樣可以根據(jù)業(yè)務(wù)需求定義更細(xì)粒度的日志分類。自定義級別需要定義級別數(shù)值和級別名稱。
高級示例:
import logging
# 定義自定義級別
logging.addLevelName(35, "TRACE") # 比DEBUG更低的級別
logging.addLevelName(45, "NOTICE") # 介于INFO和WARNING之間
def trace(self, message, *args, **kwargs):
"""TRACE級別方法"""
if self.isEnabledFor(35):
self._log(35, message, args, **kwargs)
def notice(self, message, *args, **kwargs):
"""NOTICE級別方法"""
if self.isEnabledFor(45):
self._log(45, message, args, **kwargs)
# 添加方法到Logger類
logging.Logger.trace = trace
logging.Logger.notice = notice
# 使用自定義級別
logger = logging.getLogger('app')
logger.trace("詳細(xì)的跟蹤信息")
logger.debug("調(diào)試信息")
logger.notice("需要注意的事項(xiàng)")
logger.info("常規(guī)信息")性能考慮:
- 自定義級別會增加系統(tǒng)開銷
- 合理設(shè)置級別數(shù)值,避免與現(xiàn)有級別沖突
- 考慮業(yè)務(wù)需求是否真的需要額外的級別
主題2:上下文感知的日志
什么時候需要: 需要在日志中包含請求ID、用戶ID等上下文信息時。
深入理解:
通過使用logger的adapter功能,可以輕松地為所有日志消息添加上下文信息,這樣在分布式系統(tǒng)中可以追蹤完整的請求鏈路。
高級示例:
import logging
class ContextAdapter(logging.LoggerAdapter):
"""帶上下文的Logger適配器"""
def __init__(self, logger, extra=None):
super().__init__(logger, extra or {})
def process(self, msg, kwargs):
# 為每條消息添加上下文
kwargs['extra'] = kwargs.get('extra', {})
kwargs['extra'].update(self.extra)
return msg, kwargs
# 使用示例
logger = logging.getLogger('web_service')
adapter = ContextAdapter(logger, {
'request_id': 'req-12345',
'user_id': 'user-67890',
'ip_address': '192.168.1.1'
})
# 記錄日志
adapter.info("用戶登錄成功")
adapter.error("數(shù)據(jù)庫連接失敗")
# 輸出示例:
# 2024-01-01 12:00:00 [INFO] web_service: 用戶登錄成功
# request_id=req-12345 user_id=user-67890 ip_address=192.168.1.1性能考慮:
- 上下文信息會占用內(nèi)存
- 避免在上下文中存儲大量數(shù)據(jù)
- 考慮使用線程局部變量存儲上下文
主題3:異步日志
什么時候需要: 在高并發(fā)環(huán)境下,同步日志記錄可能成為性能瓶頸時。
深入理解:
異步日志通過后臺線程處理日志寫入,避免阻塞主線程。Python 3.7+的logging模塊內(nèi)置了QueueHandler和QueueListener來實(shí)現(xiàn)異步日志。
高級示例:
import logging
import logging.handlers
import queue
import time
from threading import Thread
class AsyncLogging:
def __init__(self):
self.log_queue = queue.Queue(-1) # 無限隊(duì)列
self.setup_logger()
def setup_logger(self):
# 配置logger
logger = logging.getLogger('async_app')
logger.setLevel(logging.DEBUG)
# 創(chuàng)建QueueHandler
queue_handler = logging.handlers.QueueHandler(self.log_queue)
logger.addHandler(queue_handler)
# 創(chuàng)建QueueListener
file_handler = logging.FileHandler('async_app.log')
file_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
# 啟動監(jiān)聽器線程
self.listener = logging.handlers.QueueListener(
self.log_queue,
file_handler,
respect_handler_level=True
)
self.listener.start()
# 保存logger
self.logger = logger
def stop(self):
"""停止異步日志"""
self.logger.info("正在關(guān)閉異步日志系統(tǒng)")
self.listener.stop()
def get_logger(self):
return self.logger
# 使用異步日志
async_logger = AsyncLogging()
logger = async_logger.get_logger()
# 模擬高并發(fā)場景
def worker(worker_id):
for i in range(10):
logger.info(f"Worker {worker_id} 處理任務(wù) {i}")
time.sleep(0.1)
# 創(chuàng)建多個線程
threads = []
for i in range(5):
t = Thread(target=worker, args=(i,))
threads.append(t)
t.start()
# 等待所有線程完成
for t in threads:
t.join()
# 停止異步日志
async_logger.stop()性能考慮:
- 異步日志會增加內(nèi)存使用(隊(duì)列)
- 需要合理設(shè)置隊(duì)列大小,避免內(nèi)存溢出
- 程序退出時需要正確關(guān)閉監(jiān)聽器
- 在高吞吐量場景下,異步日志能顯著提升性能
主題4:結(jié)構(gòu)化日志
什么時候需要: 需要將日志數(shù)據(jù)發(fā)送到日志分析系統(tǒng)(如ELK Stack)時。
深入理解:
結(jié)構(gòu)化日志使用JSON等格式記錄日志,便于日志解析和分析。Python 3.9+的logging模塊支持直接輸出JSON格式的日志。
高級示例:
import logging
import json
from datetime import datetime
class JSONFormatter(logging.Formatter):
"""JSON格式的日志格式化器"""
def format(self, record):
log_data = {
'timestamp': datetime.fromtimestamp(record.created).isoformat(),
'level': record.levelname,
'logger': record.name,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno,
'thread': record.thread,
'process': record.process
}
# 添加異常信息
if record.exc_info:
log_data['exception'] = self.formatException(record.exc_info)
# 添加額外的字段
if hasattr(record, 'extra_data'):
log_data.update(record.extra_data)
return json.dumps(log_data, ensure_ascii=False)
# 配置結(jié)構(gòu)化日志
logger = logging.getLogger('structured')
logger.setLevel(logging.DEBUG)
# 文件handler
file_handler = logging.FileHandler('structured.log')
file_handler.setFormatter(JSONFormatter())
# 控制臺handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(
'%(levelname)s: %(message)s'
))
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 記錄結(jié)構(gòu)化日志
logger.info("用戶登錄", extra={
'user_id': '12345',
'ip': '192.168.1.1',
'device': 'mobile'
})
logger.error("數(shù)據(jù)庫操作失敗", exc_info=True)輸出示例(JSON):
{
"timestamp": "2024-01-01T12:00:00.123456",
"level": "INFO",
"logger": "structured",
"message": "用戶登錄",
"module": "main",
"function": "main",
"line": 42,
"thread": 12345,
"process": 67890,
"user_id": "12345",
"ip": "192.168.1.1",
"device": "mobile"
}
到此這篇關(guān)于Python logging模塊詳細(xì)教程與最佳實(shí)踐的文章就介紹到這了,更多相關(guān)Python logging模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python和shell監(jiān)控linux服務(wù)器的詳細(xì)代碼
這篇文章主要為大家介紹了使用python和shell監(jiān)控linux服務(wù)器的詳細(xì)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
python中l(wèi)strip()截掉字符的實(shí)例講解
在本篇文章里小編給大家整理的是一篇關(guān)于python中l(wèi)strip()截掉字符的實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-05-05
python 用opencv實(shí)現(xiàn)圖像修復(fù)和圖像金字塔
這篇文章主要介紹了python 如何用opencv實(shí)現(xiàn)圖像修復(fù)和圖像金字塔,幫助大家更好的理解和使用python處理圖片,感興趣的朋友可以了解下2020-11-11
詳細(xì)過程帶你用Python做車牌自動識別系統(tǒng)
這篇文章主要介紹了帶你用Python做車牌自動識別系統(tǒng)的詳細(xì)過程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08
基于Python實(shí)現(xiàn)Markdown編輯器的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何基于Python實(shí)現(xiàn)Markdown編輯器,并且可以左邊寫 Markdown,右邊實(shí)時變成漂亮網(wǎng)頁,感興趣的小伙伴可以了解下2025-08-08
計算pytorch標(biāo)準(zhǔn)化(Normalize)所需要數(shù)據(jù)集的均值和方差實(shí)例
今天小編就為大家分享一篇計算pytorch標(biāo)準(zhǔn)化(Normalize)所需要數(shù)據(jù)集的均值和方差實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
Python中Scrapy+adbapi提高數(shù)據(jù)庫寫入效率實(shí)現(xiàn)
本文主要介紹了Python中Scrapy+adbapi提高數(shù)據(jù)庫寫入效率實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10

