博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分析python日志重复输出问题
阅读量:6828 次
发布时间:2019-06-26

本文共 4781 字,大约阅读时间需要 15 分钟。

问题起源:

​ 在学习了python的函数式编程后,又接触到了logging这样一个强大的日志模块。为了减少重复代码,应该不少同学和我一样便迫不及待的写了一个自己的日志函数,比如下面这样:

import logging# 这里为了便于理解,简单的展示了一个输出到屏幕的日志函数def my_log():    logger = logging.getLogger('mysql.log')    ch = logging.StreamHandler()    ch.setLevel(logging.ERROR)    fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')    ch.setFormatter(fmt)    logger.addHandler(ch)    return loggermy_log().error('run one')my_log().error('run two')my_log().error('run three')

函数写好了,看起来似乎也没有问题,我们来运行一下!

结果如下:

2018-06-21 13:06:37,569 - mysql.log - ERROR - run one

2018-06-21 13:06:37,569 - mysql.log - ERROR - run two
2018-06-21 13:06:37,569 - mysql.log - ERROR - run two
2018-06-21 13:06:37,569 - mysql.log - ERROR - run three
2018-06-21 13:06:37,569 - mysql.log - ERROR - run three
2018-06-21 13:06:37,569 - mysql.log - ERROR - run three

日志居然重复输出了,且数量递增。


问题解析

  • 实际上logger = logging.getLogger('mysql.log')在执行时,没有每次生成一个新的logger,而是先检查内存中是否存在一个叫做‘mysql.log’的logger对象,存在则取出,不存在则新建。

  • 实例化的logger对象具有‘handlers’这样一个属性来存储 Handler,代码演示如下:

import loggingdef my_log():    logger = logging.getLogger('mysql.log')    # 每次被调用后打印出logger的handlers列表    print(logger.handlers)    ch = logging.StreamHandler()    ch.setLevel(logging.ERROR)    fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')    ch.setFormatter(fmt)    logger.addHandler(ch)    return loggermy_log().error('run one')my_log().error('run two')my_log().error('run three')

运行结果:

[]

2018-06-21 13:26:14,059 - mysql.log - ERROR - run one
[<StreamHandler <stderr> (ERROR)>]
2018-06-21 13:26:14,060 - mysql.log - ERROR - run two
2018-06-21 13:26:14,060 - mysql.log - ERROR - run two
[<StreamHandler <stderr> (ERROR)>, <StreamHandler <stderr> (ERROR)>]
2018-06-21 13:26:14,060 - mysql.log - ERROR - run three
2018-06-21 13:26:14,060 - mysql.log - ERROR - run three
2018-06-21 13:26:14,060 - mysql.log - ERROR - run three

  1.  logger.handlers 最初是一个空列表,执行 ‘logger.addHandler(ch)’ 添加一个 ‘StreamHandler’ ,输出一条日志
  2. 在第二次被调用时, logger.handlers 已经存在一个 ‘StreamHandler’ ,再次执行 ‘logger.addHandler(ch)’ 就会再次添加一个 ‘StreamHandler’ ,此时的logger有两个 ‘StreamHandler’ ,输出两条重复的日志
  3. 在第三次被调用时, logger.handlers 已经存在两个 ‘StreamHandler’ ,再次执行 ‘logger.addHandler(ch)’ 就会再次添加一个,此时的logger有三个 ‘StreamHandler’ ,输出三条重复的日志

解决办法

1.改名换姓

1 import logging 2  3  4 # 为日志函数添加一个name,每次调用时传入不同的日志名 5 def my_log(name): 6     logger = logging.getLogger(name) 7  8     ch = logging.StreamHandler() 9     ch.setLevel(logging.ERROR)10     fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')11     ch.setFormatter(fmt)12     logger.addHandler(ch)13 14     return logger15 16 17 my_log('log1').error('run one')18 my_log('log2').error('run two')19 my_log('log3').error('run three')

运行结果:

2018-06-21 13:40:51,685 - log1 - ERROR - run one

2018-06-21 13:40:51,685 - log2 - ERROR - run two
2018-06-21 13:40:51,685 - log3 - ERROR - run three

2.及时清理(logger.handlers.clear)

1 import logging 2  3  4 def my_log(): 5     logger = logging.getLogger('mysql.log') 6     # 每次被调用后,清空已经存在handler 7     logger.handlers.clear() 8  9     ch = logging.StreamHandler()10     ch.setLevel(logging.ERROR)11     fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')12     ch.setFormatter(fmt)13 14     logger.addHandler(ch)15 16     return logger17 18 19 my_log().error('run one')20 my_log().error('run two')21 my_log().error('run three')

ps:removeHandler方法(兼容性较差)

1 # 这种写法下的可以使用removeHandler方法(logger.handlers.clear也可以使用在这种写法的函数内) 2 import logging 3  4  5 def my_log(msg): 6     logger = logging.getLogger('mysql.log') 7  8     ch = logging.StreamHandler() 9     ch.setLevel(logging.ERROR)10     fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')11     ch.setFormatter(fmt)12 13     logger.addHandler(ch)14     logger.error(msg)15     16     # 在使用完ch后从移除Handler17     logger.removeHandler(ch)18 19 20 my_log('run one')21 my_log('run two')22 my_log('run three')

3.用前判断

1 import logging 2  3  4 def my_log(): 5     logger = logging.getLogger('mysql.log') 6     # 判断logger是否已经添加过handler,是则直接返回logger对象,否则执行handler设定以及addHandler(ch) 7     if not logger.handlers: 8         ch = logging.StreamHandler() 9         ch.setLevel(logging.ERROR)10         fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')11         ch.setFormatter(fmt)12 13         logger.addHandler(ch)14 15     return logger16 17 18 my_log().error('run one')19 my_log().error('run two')20 my_log().error('run three')

总结

​ 第一次遇到日志重复输出问题,那时还没有学习到面向对象编程的内容,当时并没有真正理解logging模块。学习完面向对象编程后,回过头来再思考这些问题有了豁然开朗的感觉。

​ 比如起初对logging.getLogger的实际原理不是很理解,在学习了面向对象编程中的hasattr、getattr、setattr这样一些方法后就恍然大悟了。所以诸君如果现在还是对logging模块不太理解,不妨先不纠结于这些细节,继续学下去。

​ 知识面扩充后,曾经的一些难题自然就会迎刃而解:)


参考内容:

luffycity : 

 :

转载于:https://www.cnblogs.com/quegai18/p/10031246.html

你可能感兴趣的文章
Mysql分布式部署高可用集群方案
查看>>
PHP中常用的输出语句比较?
查看>>
android&nbsp;setBackgroundColor
查看>>
UVa11181 条件概率
查看>>
<Linux> xm 命令
查看>>
linux 常用命令
查看>>
ecna 2017 J Workout for a Dumbbell (模拟)
查看>>
用Quick3.3开发微信打飞机 (二) -------------------- 子弹和敌人的配置和创建
查看>>
Tui-x 自适应屏幕 (转) ----- 6
查看>>
解题思路
查看>>
AngularJS - Apply方法监听model变化
查看>>
silverlight 添加配置项
查看>>
Linux之 VIM 编辑器
查看>>
实用网址集合
查看>>
【转】移动web资源整理
查看>>
【Linux】CentOS7下安装JDK详细过程
查看>>
(转)Hibernate 的应用(Hibernate 的结构)?
查看>>
Ubuntu terminator 多窗口终端的快捷键
查看>>
Add Binary leetcode
查看>>
关于pycharm中缩进、粘贴复制等文本编辑功能部分失效的解决办法
查看>>