更新於 2024/08/03閱讀時間約 11 分鐘

[Python]使用logging創造改良版日誌紀錄器,不重複紀錄

呈上篇,若是在大型系統中使用,重複被調用時,在每次紀錄時都會創建一個新的 FileHandler,這會導致日誌處理器不斷累積,從而使日誌重複記錄。

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

使用__new__的方法來避免重複調用

改良後

setup_logger 方法中創建一次 FileHandler,並確保不重複創建它。可以在類別中新增一個來initialized確保 FileHandler 只被創建一次。

import os
import logging
import inspect
from datetime import datetime
from threading import Lock

class WriteLogTxt:
_instances = {}
_lock = Lock()

def __new__(cls, file_path, file_name):
with cls._lock:
key = (file_path, file_name)
if key not in cls._instances:
instance = super(WriteLogTxt, cls).__new__(cls)
cls._instances[key] = instance
instance.__initialized = False
return cls._instances[key]

def __init__(self, file_path, file_name):
if self.__initialized:
return
self.file_path = file_path
self.file_name = file_name
self.logger = None
self.logger_initialized = False
self.__initialized = True

def setup_logger(self):
if not self.logger_initialized:
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] %(msg)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, datefmt='%Y/%m/%d %H:%M:%S'))

self.logger = logging.getLogger(f"{self.file_path}/{self.file_name}")
self.logger.setLevel(logging.INFO)
self.logger.addHandler(file_handler)
self.logger_initialized = True

def write_log_info(self, log_content):
self.setup_logger()
caller_frame = inspect.stack()[1]
caller_filename = caller_frame.filename
caller_lineno = caller_frame.lineno
match = os.path.basename(caller_filename)
self.logger.info(f"[File name :{match}][Line :{caller_lineno}]{log_content}")

def write_log_warning(self, log_content):
self.setup_logger()
caller_frame = inspect.stack()[1]
caller_filename = caller_frame.filename
caller_lineno = caller_frame.lineno
match = os.path.basename(caller_filename)
self.logger.warning(f"[File name :{match}][Line :{caller_lineno}]{log_content}")


# 創建並設置日誌記錄器
for i in range(10):
    log_obj1 = WriteLogTxt('D:/crab/crab/log', 'Log')
    log_obj1.setup_logger()
    log_obj1.write_log_info("This is a log message")

即使不斷重複被調用類別,也只會被創建一次,不會在有重複紀錄的bug

針對__new__ 方法與__init__ 方法 更詳細的說明

在 Python 中,__new__ 方法負責創建一個新的實例。這個方法在 __init__ 方法之前被調用,它允許我們控制對象的創建過程。

這段程式碼使用 __new__ 方法來實現單例模式,以確保每個 file_pathfile_name 組合只會創建一個實例。

詳細說明如下:

__new__ 方法

def __new__(cls, file_path, file_name):
with cls._lock:
key = (file_path, file_name)
if key not in cls._instances:
instance = super(WriteLogTxt, cls).__new__(cls)
cls._instances[key] = instance
instance.__initialized = False
return cls._instances[key]
  • cls: 代表這個類本身,它在類方法中被用作第一個參數。
  • file_pathfile_name: 用於唯一標識每個日誌實例的路徑和文件名。

步驟解析

  1. 加鎖with cls._lock: 使用 Lock 來確保此方法在多線程環境下是線程安全的。
  2. 生成唯一鍵key = (file_path, file_name),通過組合 file_pathfile_name 來生成一個唯一鍵。
  3. 檢查實例存在if key not in cls._instances:,如果這個唯一鍵不在 _instances 字典中,則創建一個新的實例。
  4. 創建實例instance = super(WriteLogTxt, cls).__new__(cls) 使用 super() 調用基類(即 object)的 __new__ 方法來創建實例。
  5. 存儲實例cls._instances[key] = instance 將新創建的實例存儲在 _instances 字典中。
  6. 設置初始化標記instance.__initialized = False,設置 __initialized 標記為 False,表示實例尚未被初始化。
  7. 返回實例return cls._instances[key] 返回對應鍵的實例,保證每個鍵只有一個實例。


__init__ 方法

__init__ 方法負責初始化新創建的實例。

當一個實例被創建後(即調用了 __new__ 方法後),__init__ 方法被調用來初始化該實例。詳細說明如下:

def __init__(self, file_path, file_name):
if self.__initialized:
return
self.file_path = file_path
self.file_name = file_name
self.logger = None
self.logger_initialized = False
self.__initialized = True

步驟解析

  1. 檢查是否已初始化if self.__initialized: 如果 __initialized 標記為 True,表示這個實例已經被初始化過了,直接返回,不做重複初始化。
  2. 設置屬性
    • self.file_path = file_path:設置實例的文件路徑屬性。
    • self.file_name = file_name:設置實例的文件名屬性。
    • self.logger = None:初始化 logger 屬性為 None。
    • self.logger_initialized = False:初始化 logger_initialized 標記為 False,表示日誌記錄器尚未初始化。
  3. 設置初始化標記self.__initialized = True,設置 __initialized 標記為 True,表示實例已經被初始化。



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