2024-05-17|閱讀時間 ‧ 約 29 分鐘

[Python]使用多個logging如何追蹤呼叫它的函式及行數

呈上次使用logging來撰寫日誌,利用類別包裝的方式,可實現多個日誌紀錄器,但發現這樣就失去它原先,可以回傳是誰呼叫他並記錄行數的功能。

[Python]使用logging創建兩個以上的日誌紀錄

若開啟函式名稱、行數及訊息的功能,就會像這樣,幾乎都是記錄到,我定義中類別的函式,兩個檔案都是寫一樣的內容,就達不到,我們想要的結果。

那要怎麼解呢?


若不是要實現多個logging紀錄器,加上以下程式碼,不要用類別包裝,直接用logging這樣就可以很方便地去使用logging的套件就好。

# 設定日誌格式,包含日期時間、日誌等級、函式名稱、行數及訊息
log_format = "%(asctime)s - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s"

在 Python 中,可以使用 inspect 模組來取得目前被呼叫的函式名稱和行數。

我們利用inspect 模組來實現追蹤呼叫logging的檔案名稱及行數。

完整程式範例

import os
import re
import logging
import inspect
from datetime import datetime

class WriteLogTxt:
'''
setup_logger 設置logging格式及創立文件夾
write_log_info 指定檔案名稱及log內容輸入
write_log_warning
'''
def __init__(self,file_path,file_name):
self.file_path = file_path
self.file_name = file_name

def setup_logger(self):
'''
設置logging格式及創立文件夾
'''
now = datetime.now()
year_month = now.strftime("%Y-%m") #取得字符串
log_folder = os.path.join(self.file_path, year_month)
# 檢查文件夾與文件是否存在
if not os.path.exists(log_folder):
os.makedirs(log_folder)
# 設置格式
# 設定日誌格式,包含日期時間、日誌等級、函式名稱、行數及訊息
log_format = "%(asctime)s - %(levelname)s - %(message)s"
file_handler = logging.FileHandler(os.path.join(log_folder, f"{self.file_name}_{now.date()}.log"))
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(log_format))

# 設置logger
self.logger = logging.getLogger(self.file_name)
self.logger.setLevel(logging.INFO)
self.logger.addHandler(file_handler)

# 指定檔案名稱及log內容輸入
def write_log_info(self,log_content):
'''
log_content : Log
'''
# 寫入log
# 獲取呼叫者的資訊
caller_frame = inspect.currentframe().f_back
frame_info = inspect.getframeinfo(caller_frame)
file_path = frame_info.filename
line_number = frame_info.lineno
pattern = r'[^\\]+$'
file_name = re.search(pattern, file_path).group(0)
self.logger.info(f"file_name: [{file_name}],line_number: [{line_number}] {log_content}")

額外開啟兩個py,來呼叫WriteLogTxt撰寫日誌紀錄

取名為module_1.py與module_2.py

from test import WriteLogTxt

#調用第一個log_obj寫入log
log_obj = WriteLogTxt('D:/CRABpy/write/log','sample_1')
log_obj.setup_logger()
log_obj.write_log_info('test_1')
from test import WriteLogTxt

#調用第二個log_obj寫入log
log_obj_2 = WriteLogTxt('D:/CRABpy/write/log','sample_2')
log_obj_2.setup_logger()
log_obj_2.write_log_info('test_2')

成功實現了,追蹤呼叫logging的檔案名稱及行數。

主要是新增了以下程式碼

# 獲取呼叫者的資訊
caller_frame = inspect.currentframe().f_back
frame_info = inspect.getframeinfo(caller_frame)
file_path = frame_info.filename
line_number = frame_info.lineno
pattern = r'[^\\]+$'
file_name = re.search(pattern, file_path).group(0)

詳細說明

  • 獲取當前框架:
    caller_frame = inspect.currentframe().f_back
    inspect.currentframe() 函式返回當前的堆疊框架物件。f_back 屬性指向上一個堆疊框架,這裡的 caller_frame 即為呼叫當前函式的框架。
  • 獲取框架資訊:
    frame_info = inspect.getframeinfo(caller_frame)
    inspect.getframeinfo(frame) 函式接收一個框架物件並返回一個包含檔案名稱、行數、函式名稱等資訊的物件。這裡的 frame_info 包含呼叫函式所在的檔案名稱和行數。
  • 提取檔案路徑:
    python複製程式碼file_path = frame_info.filename
    frame_info.filename 返回呼叫函式的檔案路徑。
  • 提取行數:
    line_number = frame_info.lineno
    frame_info.lineno 返回呼叫函式所在的行數。
  • 定義正規表達式模式:
    pattern = r'[^\\]+$'
    這個正規表達式模式 [^\\]+$ 匹配最後一個反斜杠(\)之後的所有字符,直到字符串的結尾。具體來說:
    • [^\\] 匹配任何不是反斜杠的字符。
    • + 匹配前一個字符集合的一次或多次出現。
    • $ 匹配字符串的結尾。
  • 應用正規表達式提取檔案名稱:
    file_name = re.search(pattern, file_path).group(0)
    re.search(pattern, file_path) 尋找符合模式的第一個匹配,返回一個 Match 物件。group(0) 返回整個匹配的字符串,即檔案名稱。

這段程式碼的目的是從呼叫函式的框架中提取檔案名稱和行數,並使用正規表達式從完整檔案路徑中擷取出檔案名稱。例如,給定完整路徑 'f:\\python\\opencv\\pathinputdata\\module_1.py',它會提取出 module_1.py


分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.