gpt4 book ai didi

解决python logging遇到的坑 日志重复打印问题

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 35 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章解决python logging遇到的坑 日志重复打印问题由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

python 中 logging模块 假如遇到 多线程 或者 多进程 或者在web框架中自定义logging的话(一个请求就是一个独立的线程)非常容易重复打印日志 和造成内存崩溃,所以:

解决方法如下:

重写日志方法 用类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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的简单封装.

如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/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 。

?
1
2
3
4
5
6
#!/usr/bin/python
# -*- coding:utf-8 -*-
 
from common.log import Log
log = Log().getlog()
log.info( "I am a.py" )

b.py 。

?
1
2
3
4
5
6
#!/usr/bin/python
# -*- coding:utf-8 -*-
 
from common.log import Log
log = Log().getlog()
log.info( "I am b.py" )

c.py 。

?
1
2
3
4
5
6
7
8
#!/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 。

?
1
2
3
from common.log import Log
log = Log(__name__).getlog()
log.info( "I am a.py" )

b.py 。

?
1
2
3
from common.log import Log
log = Log(__name__).getlog()
log.info( "I am b.py" )

c.py 。

?
1
2
3
4
5
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 。

没有重复了,符合预期。问题得以解决.

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我。如有错误或未考虑完全的地方,望不吝赐教.

原文链接:https://blog.csdn.net/Chelydra/article/details/79850366 。

最后此篇关于解决python logging遇到的坑 日志重复打印问题的文章就讲到这里了,如果你想了解更多关于解决python logging遇到的坑 日志重复打印问题的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com