解決python logging遇到的坑 日志重復打印問題
python 中 logging模塊 假如遇到 多線程 或者 多進程 或者在web框架中自定義logging的話(一個請求就是一個獨立的線程)非常容易重復打印日志 和造成內存崩潰,所以:
解決方法如下:
重寫日志方法 用類:
class Log(): import logging def __init__(self): self.logger = logging.getLogger(__name__) # 以下三行為清空上次文件 # 這為清空當前文件的logging 因為logging會包含所有的文件的logging logging.Logger.manager.loggerDict.pop(__name__) # 將當前文件的handlers 清空 self.logger.handlers = [] # 然後再次移除當前文件logging配置 self.logger.removeHandler(self.logger.handlers) # 這裡進行判斷,如果logger.handlers列表為空,則添加,否則,直接去寫日志 if not self.logger.handlers: # loggger 文件配置路徑 self.handler = logging.FileHandler(os.getcwd() + '/logger/%s_log/%s_score.log' % (str(dt.date.today()), str(dt.date.today()))) # logger 配置等級 self.logger.setLevel(logging.DEBUG) # logger 輸出格式 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s') # 添加輸出格式進入handler self.handler.setFormatter(formatter) # 添加文件設置金如handler self.logger.addHandler(self.handler) # 以下皆為重寫方法 並且每次記錄後清除logger def info(self,message=None): self.__init__() self.logger.info(message) self.logger.removeHandler(self.logger.handlers) def debug(self,message=None): self.__init__() self.logger.debug(message) self.logger.removeHandler(self.logger.handlers) def warning(self,message=None): self.__init__() self.logger.warning(message) self.logger.removeHandler(self.logger.handlers) def error(self,message=None): self.__init__() self.logger.error(message) self.logger.removeHandler(self.logger.handlers) def critical(self, message=None): self.__init__() self.logger.critical(message) self.logger.removeHandler(self.logger.handlers)
親測有效!
另外 模塊尤其註意 例如web請求的時候 在接口處調用 然後引導傳參 千萬別做全局變量
補充:python中多個文件共用logger,重復打印問題的解決方案
問題背景&現象
最近在項目中,需要用python的logging庫來將日志打印到文件中,然後將python腳本放到crontab中執行。所以寫瞭一個logger的簡單封裝。
如下:
#!/usr/bin/python # -*- coding:utf-8 -*- import logging import time import os class Log(object): ''' 封裝後的logging ''' def __init__(self, logger=None, log_cate='search'): ''' 指定保存日志的文件路徑,日志級別,以及調用文件 將日志存入到指定的文件中 ''' # 創建一個logger self.logger = logging.getLogger(logger) self.logger.setLevel(logging.DEBUG) # 創建一個handler,用於寫入日志文件 self.log_time = time.strftime("%Y_%m_%d") file_dir = os.getcwd() + '/../log' if not os.path.exists(file_dir): os.mkdir(file_dir) self.log_path = file_dir self.log_name = self.log_path + "/" + log_cate + "." + self.log_time + '.log' # print(self.log_name) fh = logging.FileHandler(self.log_name, 'a') # 追加模式 這個是python2的 # fh = logging.FileHandler(self.log_name, 'a', encoding='utf-8') # 這個是python3的 fh.setLevel(logging.INFO) # 再創建一個handler,用於輸出到控制臺 ch = logging.StreamHandler() ch.setLevel(logging.INFO) # 定義handler的輸出格式 formatter = logging.Formatter( '[%(asctime)s] %(filename)s->%(funcName)s line:%(lineno)d [%(levelname)s]%(message)s') fh.setFormatter(formatter) ch.setFormatter(formatter) # 給logger添加handler self.logger.addHandler(fh) self.logger.addHandler(ch) # 添加下面一句,在記錄日志之後移除句柄 # self.logger.removeHandler(ch) # self.logger.removeHandler(fh) # 關閉打開的文件 fh.close() ch.close() def getlog(self): return self.logger
目的是讓所有用到logger的地方,隻import這個封裝庫就行,然後直接調用。比如調用logger的
a.py
#!/usr/bin/python # -*- coding:utf-8 -*- from common.log import Log log = Log().getlog() log.info("I am a.py")
b.py
#!/usr/bin/python # -*- coding:utf-8 -*- from common.log import Log log = Log().getlog() log.info("I am b.py")
c.py
#!/usr/bin/python # -*- coding:utf-8 -*- import a import b from common.log import Log log = Log().getlog() log.info("I am c.py")
此時執行c.py的結果如下:
➜ search git:(master) ✗ python c.py
[2019-01-14 15:58:35,807] a.py-><module> line:6 [INFO]I am a.py
[2019-01-14 15:58:35,808] b.py-><module> line:6 [INFO]I am b.py
[2019-01-14 15:58:35,808] b.py-><module> line:6 [INFO]I am b.py
[2019-01-14 15:58:35,809] c.py-><module> line:8 [INFO]I am c.py
[2019-01-14 15:58:35,809] c.py-><module> line:8 [INFO]I am c.py
[2019-01-14 15:58:35,809] c.py-><module> line:8 [INFO]I am c.py
可見,a.py, b.py,c.py的logger共用瞭,出現瞭重復打印。
問題原因分析
從現象可以得出,不同文件間的log系統是相互影響的,在a.py,b.py, c.py中,我們的調用方式是log = Log().getlog(), 即self.logger = logging.getLogger(logger),logger參數並未傳遞 , 所以得到的self.logger是RootLogger。
RootLogger是一個python程序內全局唯一的,所有Logger對象的祖先。所以我們對RootLogger的設定,自然會影響到所有的日志輸出。簡言之,就是先打開的文件中對log的設置,後打開的文件都會受到影響,都會走一遍logger的繼承關系。在這個示例中,b.py在a.py之後被import, 所以b.py會執行一次自己的logger,再執行一次a.py中打開的RootLogger, 以此類推………
問題解決方式
不用默認的RootLogger, 給每個Logger都加個名字。
a.py
from common.log import Log log = Log(__name__).getlog() log.info("I am a.py")
b.py
from common.log import Log log = Log(__name__).getlog() log.info("I am b.py")
c.py
import b import a from common.log import Log log = Log(__name__).getlog() log.info("I am c.py")
c.py的最新執行結果:
➜ search git:(master) ✗ python c.py
[2019-01-14 16:24:12,008] b.py-><module> line:6 [INFO]I am b.py
[2019-01-14 16:24:12,009] a.py-><module> line:6 [INFO]I am a.py
[2019-01-14 16:24:12,009] c.py-><module> line:10 [INFO]I am c.py
沒有重復瞭,符合預期。問題得以解決。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- python 日志模塊logging的使用場景及示例
- python自動化測試通過日志3分鐘定位bug
- 如何理解python接口自動化之logging日志模塊
- Python標準庫中的logging用法示例詳解
- Python 內置logging 使用詳細介紹