python 中 logging模塊 假如遇到 多線程 或者 多進(jìn)程 或者在web框架中自定義logging的話(一個請求就是一個獨(dú)立的線程)非常容易重復(fù)打印日志 和造成內(nèi)存崩潰,所以:
解決方法如下:
重寫日志方法 用類:
class Log():
import logging
def __init__(self):
self.logger = logging.getLogger(__name__)
# 以下三行為清空上次文件
# 這為清空當(dāng)前文件的logging 因?yàn)閘ogging會包含所有的文件的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列表為空,則添加,否則,直接去寫日志
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')
# 添加輸出格式進(jìn)入handler
self.handler.setFormatter(formatter)
# 添加文件設(shè)置金如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請求的時候 在接口處調(diào)用 然后引導(dǎo)傳參 千萬別做全局變量
補(bǔ)充:python中多個文件共用logger,重復(fù)打印問題的解決方案
問題背景現(xiàn)象
最近在項(xiàng)目中,需要用python的logging庫來將日志打印到文件中,然后將python腳本放到crontab中執(zhí)行。所以寫了一個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'):
'''
指定保存日志的文件路徑,日志級別,以及調(diào)用文件
將日志存入到指定的文件中
'''
# 創(chuàng)建一個logger
self.logger = logging.getLogger(logger)
self.logger.setLevel(logging.DEBUG)
# 創(chuàng)建一個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)
# 再創(chuàng)建一個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)
# 關(guān)閉打開的文件
fh.close()
ch.close()
def getlog(self):
return self.logger
目的是讓所有用到logger的地方,只import這個封裝庫就行,然后直接調(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")
此時執(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
可見,a.py, b.py,c.py的logger共用了,出現(xiàn)了重復(fù)打印。
問題原因分析
從現(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是一個python程序內(nèi)全局唯一的,所有Logger對象的祖先。所以我們對RootLogger的設(shè)定,自然會影響到所有的日志輸出。簡言之,就是先打開的文件中對log的設(shè)置,后打開的文件都會受到影響,都會走一遍logger的繼承關(guān)系。在這個示例中,b.py在a.py之后被import, 所以b.py會執(zhí)行一次自己的logger,再執(zhí)行一次a.py中打開的RootLogger, 以此類推.........
問題解決方式
不用默認(rèn)的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的最新執(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
沒有重復(fù)了,符合預(yù)期。問題得以解決。
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- python 實(shí)現(xiàn)logging動態(tài)變更輸出日志文件名
- python (logging) 日志按日期、大小回滾的操作
- Python日志打印里logging.getLogger源碼分析詳解
- python 如何對logging日志封裝
- Python logging自定義字段輸出及打印顏色
- Python中l(wèi)ogging日志的四個等級和使用
- Python+logging輸出到屏幕將log日志寫入文件
- Python logging模塊handlers用法詳解
- 如何理解python接口自動化之logging日志模塊