解決python logging遇到的坑 日志重復(fù)打印問(wèn)題
python 中 logging模塊 假如遇到 多線程 或者 多進(jìn)程 或者在web框架中自定義logging的話(huà)(一個(gè)請(qǐng)求就是一個(gè)獨(dú)立的線程)非常容易重復(fù)打印日志 和造成內(nèi)存崩潰,所以:
解決方法如下:
重寫(xiě)日志方法 用類(lèi):
class Log(): import logging def __init__(self): self.logger = logging.getLogger(__name__) # 以下三行為清空上次文件 # 這為清空當(dāng)前文件的logging 因?yàn)閘ogging會(huì)包含所有的文件的logging logging.Logger.manager.loggerDict.pop(__name__) # 將當(dāng)前文件的handlers 清空 self.logger.handlers = [] # 然后再次移除當(dāng)前文件logging配置 self.logger.removeHandler(self.logger.handlers) # 這里進(jìn)行判斷,如果logger.handlers列表為空,則添加,否則,直接去寫(xiě)日志 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 配置等級(jí) self.logger.setLevel(logging.DEBUG) # logger 輸出格式 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s') # 添加輸出格式進(jìn)入handler self.handler.setFormatter(formatter) # 添加文件設(shè)置金如handler self.logger.addHandler(self.handler) # 以下皆為重寫(xiě)方法 并且每次記錄后清除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)
親測(cè)有效!
另外 模塊尤其注意 例如web請(qǐng)求的時(shí)候 在接口處調(diào)用 然后引導(dǎo)傳參 千萬(wàn)別做全局變量
補(bǔ)充:python中多個(gè)文件共用logger,重復(fù)打印問(wèn)題的解決方案
問(wèn)題背景&現(xiàn)象
最近在項(xiàng)目中,需要用python的logging庫(kù)來(lái)將日志打印到文件中,然后將python腳本放到crontab中執(zhí)行。所以寫(xiě)了一個(gè)logger的簡(jiǎn)單封裝。
如下:
#!/usr/bin/python # -*- coding:utf-8 -*- import logging import time import os class Log(object): ''' 封裝后的logging ''' def __init__(self, logger=None, log_cate='search'): ''' 指定保存日志的文件路徑,日志級(jí)別,以及調(diào)用文件 將日志存入到指定的文件中 ''' # 創(chuàng)建一個(gè)logger self.logger = logging.getLogger(logger) self.logger.setLevel(logging.DEBUG) # 創(chuàng)建一個(gè)handler,用于寫(xiě)入日志文件 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') # 追加模式 這個(gè)是python2的 # fh = logging.FileHandler(self.log_name, 'a', encoding='utf-8') # 這個(gè)是python3的 fh.setLevel(logging.INFO) # 再創(chuàng)建一個(gè)handler,用于輸出到控制臺(tái) 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) # 關(guān)閉打開(kāi)的文件 fh.close() ch.close() def getlog(self): return self.logger
目的是讓所有用到logger的地方,只import這個(gè)封裝庫(kù)就行,然后直接調(diào)用。比如調(diào)用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")
此時(shí)執(zhí)行c.py的結(jié)果如下:
➜ 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
可見(jiàn),a.py, b.py,c.py的logger共用了,出現(xiàn)了重復(fù)打印。
問(wèn)題原因分析
從現(xiàn)象可以得出,不同文件間的log系統(tǒng)是相互影響的,在a.py,b.py, c.py中,我們的調(diào)用方式是log = Log().getlog(), 即self.logger = logging.getLogger(logger),logger參數(shù)并未傳遞 , 所以得到的self.logger是RootLogger。
RootLogger是一個(gè)python程序內(nèi)全局唯一的,所有Logger對(duì)象的祖先。所以我們對(duì)RootLogger的設(shè)定,自然會(huì)影響到所有的日志輸出。簡(jiǎn)言之,就是先打開(kāi)的文件中對(duì)log的設(shè)置,后打開(kāi)的文件都會(huì)受到影響,都會(huì)走一遍logger的繼承關(guān)系。在這個(gè)示例中,b.py在a.py之后被import, 所以b.py會(huì)執(zhí)行一次自己的logger,再執(zhí)行一次a.py中打開(kāi)的RootLogger, 以此類(lèi)推.........
問(wèn)題解決方式
不用默認(rèn)的RootLogger, 給每個(gè)Logger都加個(gè)名字。
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的最新執(zhí)行結(jié)果:
➜ 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
沒(méi)有重復(fù)了,符合預(yù)期。問(wèn)題得以解決。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
python對(duì) MySQL 數(shù)據(jù)庫(kù)進(jìn)行增刪改查的腳本
這篇文章主要介紹了python對(duì) MySQL 數(shù)據(jù)庫(kù)進(jìn)行增刪改查的腳本,幫助大家更好的利用python處理數(shù)據(jù)庫(kù),感興趣的朋友可以了解下2020-10-10python計(jì)算無(wú)向圖節(jié)點(diǎn)度的實(shí)例代碼
今天小編就為大家分享一篇python計(jì)算無(wú)向圖節(jié)點(diǎn)度的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11Python?PySpark案例實(shí)戰(zhàn)教程
PySpark是由Spark官方開(kāi)發(fā)的Python語(yǔ)言第三方庫(kù),Python開(kāi)發(fā)者可以使用pip程序快速的安裝PySpark并像其它三方庫(kù)那樣直接使用,本文給大家介紹Python?PySpark案例實(shí)戰(zhàn),感興趣的朋友一起看看吧2023-09-09Django實(shí)現(xiàn)微信小程序支付的示例代碼
這篇文章主要介紹了Django實(shí)現(xiàn)微信小程序支付的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Python開(kāi)源自動(dòng)化工具Playwright安裝及介紹使用
playwright-python是一個(gè)強(qiáng)大的Python庫(kù),僅用一個(gè)API即可自動(dòng)執(zhí)行Chromium、Firefox、WebKit等主流瀏覽器自動(dòng)化操作,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2021-12-128個(gè)實(shí)用的Python程序你知道幾個(gè)
這篇文章主要為大家詳細(xì)介紹了8個(gè)實(shí)用的Python程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助<BR>2022-02-02Python二叉搜索樹(shù)與雙向鏈表轉(zhuǎn)換實(shí)現(xiàn)方法
這篇文章主要介紹了Python二叉搜索樹(shù)與雙向鏈表轉(zhuǎn)換實(shí)現(xiàn)方法,涉及Python二叉搜索樹(shù)的定義、實(shí)現(xiàn)以及雙向鏈表的轉(zhuǎn)換技巧,需要的朋友可以參考下2016-04-04python 讀入多行數(shù)據(jù)的實(shí)例
下面小編就為大家分享一篇python 讀入多行數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04在Python中通過(guò)threading模塊定義和調(diào)用線程的方法
由于著名的GIL的存在,Python中雖然能創(chuàng)建多條線程,但卻不能同時(shí)執(zhí)行...anyway,這里我們還是來(lái)學(xué)習(xí)一下在Python中通過(guò)threading模塊定義和調(diào)用線程的方法2016-07-07