呈上篇,若是在大型系統中使用,重複被調用時,在每次紀錄時都會創建一個新的 FileHandler
,這會導致日誌處理器不斷累積,從而使日誌重複記錄。
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
在 Python 中,__new__
方法負責創建一個新的實例。這個方法在 __init__
方法之前被調用,它允許我們控制對象的創建過程。
這段程式碼使用 __new__
方法來實現單例模式,以確保每個 file_path
和 file_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_path
和 file_name
: 用於唯一標識每個日誌實例的路徑和文件名。with cls._lock:
使用 Lock
來確保此方法在多線程環境下是線程安全的。key = (file_path, file_name)
,通過組合 file_path
和 file_name
來生成一個唯一鍵。if key not in cls._instances:
,如果這個唯一鍵不在 _instances
字典中,則創建一個新的實例。instance = super(WriteLogTxt, cls).__new__(cls)
使用 super()
調用基類(即 object
)的 __new__
方法來創建實例。cls._instances[key] = instance
將新創建的實例存儲在 _instances
字典中。instance.__initialized = False
,設置 __initialized
標記為 False
,表示實例尚未被初始化。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
if self.__initialized:
如果 __initialized
標記為 True
,表示這個實例已經被初始化過了,直接返回,不做重複初始化。self.__initialized = True
,設置 __initialized
標記為 True
,表示實例已經被初始化。