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

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

呈上篇,若是在大型系統中使用,重複被調用時,在每次紀錄時都會創建一個新的 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

raw-image

針對__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,表示實例已經被初始化。



avatar-img
128會員
209內容數
本業是影像辨識軟體開發,閒暇時間進修AI相關內容,將學習到的內容寫成文章分享。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
螃蟹_crab的沙龍 的其他內容
在處理數據時,最可能會遇到數據中含有None的時候,若沒有處理就進行運算就會造成程式崩潰或者報錯 數據中含有None input_list = [(42, 292), (28, 296), (999, 92), (993, 46), (219, 4), (279, 2), (None, None
在檢查列表中含有tuple的座標點時,若要給其他演算法做運算時若有其中有tuple有空值時,就會報錯。 本文主要介紹兩種方法可以檢查是否有空值 程式範例1 positon_list =[(42,123),(None,None),(22,11)] for cord in positon_lis
對於程式卡頓的問題,如何分析程式碼占用多少記憶體,如何釋放或改寫,可以先用python內建的tracemalloc模組來追蹤 Python 分配的記憶體區塊。 本文將介紹最簡單的用法,來分析一段程式碼占用了多少記憶體。 結果呈現 印出當前使用的記憶體,與峰值記憶體使用量。 程式範例 i
在離線環境需要安裝Python套件時就相當的麻煩,需要先下載好套件包,在打指令安裝,若套件數量一多時就會相當麻煩。 本文將介紹如何利用兩行指令快速的安裝整個資料夾的套件。
在讀取檔案時,最怕路徑的問題,常常會有路徑錯誤造成的異常報錯。 為了避免諸如此類的問題發生,明白程式的當前目錄與檔案的路徑是很重要的。 可以利用os 模組是 Python 中的一個標準庫,提供了許多與操作系統的功能。 以下是一些常用的 os 模組基本操作及其範例: 1. os.getcwd
解讀JSON 字串 首先,你需要使用 Python 的 json 模組來解讀JSON 字串。 JSON的基本結構: 由花括號 {} 包圍,內部是鍵值對的集合,每個鍵值對之間用逗號分隔。 鍵是字串類型,值可以是任何JSON支持的資料類型(字串、數字、布林值、陣列、物件或 null)。 {
在處理數據時,最可能會遇到數據中含有None的時候,若沒有處理就進行運算就會造成程式崩潰或者報錯 數據中含有None input_list = [(42, 292), (28, 296), (999, 92), (993, 46), (219, 4), (279, 2), (None, None
在檢查列表中含有tuple的座標點時,若要給其他演算法做運算時若有其中有tuple有空值時,就會報錯。 本文主要介紹兩種方法可以檢查是否有空值 程式範例1 positon_list =[(42,123),(None,None),(22,11)] for cord in positon_lis
對於程式卡頓的問題,如何分析程式碼占用多少記憶體,如何釋放或改寫,可以先用python內建的tracemalloc模組來追蹤 Python 分配的記憶體區塊。 本文將介紹最簡單的用法,來分析一段程式碼占用了多少記憶體。 結果呈現 印出當前使用的記憶體,與峰值記憶體使用量。 程式範例 i
在離線環境需要安裝Python套件時就相當的麻煩,需要先下載好套件包,在打指令安裝,若套件數量一多時就會相當麻煩。 本文將介紹如何利用兩行指令快速的安裝整個資料夾的套件。
在讀取檔案時,最怕路徑的問題,常常會有路徑錯誤造成的異常報錯。 為了避免諸如此類的問題發生,明白程式的當前目錄與檔案的路徑是很重要的。 可以利用os 模組是 Python 中的一個標準庫,提供了許多與操作系統的功能。 以下是一些常用的 os 模組基本操作及其範例: 1. os.getcwd
解讀JSON 字串 首先,你需要使用 Python 的 json 模組來解讀JSON 字串。 JSON的基本結構: 由花括號 {} 包圍,內部是鍵值對的集合,每個鍵值對之間用逗號分隔。 鍵是字串類型,值可以是任何JSON支持的資料類型(字串、數字、布林值、陣列、物件或 null)。 {
你可能也想看
Google News 追蹤
Thumbnail
本文探討了複利效應的重要性,並藉由巴菲特的投資理念,說明如何選擇穩定產生正報酬的資產及長期持有的核心理念。透過定期定額的投資方式,不僅能減少情緒影響,還能持續參與全球股市的發展。此外,文中介紹了使用國泰 Cube App 的便利性及低手續費,幫助投資者簡化投資流程,達成長期穩定增長的財務目標。
Thumbnail
20240802   這裡在挑戰寫365篇咚咚,會記錄我的日常,可能是生活、是閱讀、是回憶、是思考,都小小的,歡迎你來,也歡迎你看,也歡迎跟我說說話。 如果你願意陪著我一起挑戰,我會非常非常高興並且感謝。
Thumbnail
透過每日紀錄寫下工作與生活的點滴,反思自我的管理與處理狀況以及工作效率與成就感等部分。計畫進行屋內裝潢也是從生活紀錄開始的新計劃。同時也透過視頻學習職場處事的思考脈絡,以提升自己的工作方式。這篇日記著重於工作與生活間的反思和成長。
  筆記軟體裡累積了不少構思文章留下的廢案。沒寫的原因多半是因為覺得有資料要查,當下沒什麼時間,所以想說之後有空再弄。但我們都知道所謂「有空再弄」往往會走向怎樣的結果:它會一直放著、然後越積越多。
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
瞭解如何在Xcode15及以上使用Logger進行更好的程式debug。Logger可以更好的組織Log,但也有一些缺點需要注意。本文將介紹Logger的基本使用方式,以及一些注意事項。
Thumbnail
在數位筆記管理中,標籤混亂是一個常見問題,尤其是當出現許多重複標籤時。例如,“#人力資源”和“#人力資源管理”,“#旅行”和“#旅遊”,“#啟發”和“#啟示”這些類似的標籤會導致分類繁複而臃腫,長期下來不僅無益反而有害。
Thumbnail
工具功能 (1) 彈性任意查詢檔案,如對來源目錄設定,檔案修改日期 設定,檔名特定字串或副檔名設定後,自動查出明細,並可展開至各階子目錄處理     (2) 依查詢後結果,可產出 LIST ,提供查詢結果之確認,再依此對檔案作複 (3) 可對檔案作移動,複製至別處,刪除處理,使電腦可騰出硬碟空間
Thumbnail
大數據時代下,Log的多元應用至關重要。Log生成龐大,格式各異,特別金融業需合規。探討Log廣泛應用、資訊安全、IT管理和商業決策。建立Log管理系統核心深入法規,強化IT治理、權限控管。一站式Log管理平台,確保資訊安全合規。
Thumbnail
關於多執行緒/多行程的使用方式 在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼: imp
Thumbnail
本文探討了複利效應的重要性,並藉由巴菲特的投資理念,說明如何選擇穩定產生正報酬的資產及長期持有的核心理念。透過定期定額的投資方式,不僅能減少情緒影響,還能持續參與全球股市的發展。此外,文中介紹了使用國泰 Cube App 的便利性及低手續費,幫助投資者簡化投資流程,達成長期穩定增長的財務目標。
Thumbnail
20240802   這裡在挑戰寫365篇咚咚,會記錄我的日常,可能是生活、是閱讀、是回憶、是思考,都小小的,歡迎你來,也歡迎你看,也歡迎跟我說說話。 如果你願意陪著我一起挑戰,我會非常非常高興並且感謝。
Thumbnail
透過每日紀錄寫下工作與生活的點滴,反思自我的管理與處理狀況以及工作效率與成就感等部分。計畫進行屋內裝潢也是從生活紀錄開始的新計劃。同時也透過視頻學習職場處事的思考脈絡,以提升自己的工作方式。這篇日記著重於工作與生活間的反思和成長。
  筆記軟體裡累積了不少構思文章留下的廢案。沒寫的原因多半是因為覺得有資料要查,當下沒什麼時間,所以想說之後有空再弄。但我們都知道所謂「有空再弄」往往會走向怎樣的結果:它會一直放著、然後越積越多。
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
瞭解如何在Xcode15及以上使用Logger進行更好的程式debug。Logger可以更好的組織Log,但也有一些缺點需要注意。本文將介紹Logger的基本使用方式,以及一些注意事項。
Thumbnail
在數位筆記管理中,標籤混亂是一個常見問題,尤其是當出現許多重複標籤時。例如,“#人力資源”和“#人力資源管理”,“#旅行”和“#旅遊”,“#啟發”和“#啟示”這些類似的標籤會導致分類繁複而臃腫,長期下來不僅無益反而有害。
Thumbnail
工具功能 (1) 彈性任意查詢檔案,如對來源目錄設定,檔案修改日期 設定,檔名特定字串或副檔名設定後,自動查出明細,並可展開至各階子目錄處理     (2) 依查詢後結果,可產出 LIST ,提供查詢結果之確認,再依此對檔案作複 (3) 可對檔案作移動,複製至別處,刪除處理,使電腦可騰出硬碟空間
Thumbnail
大數據時代下,Log的多元應用至關重要。Log生成龐大,格式各異,特別金融業需合規。探討Log廣泛應用、資訊安全、IT管理和商業決策。建立Log管理系統核心深入法規,強化IT治理、權限控管。一站式Log管理平台,確保資訊安全合規。
Thumbnail
關於多執行緒/多行程的使用方式 在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼: imp