python logging重复记录日志问题的解决方法


Posted in Python onJuly 12, 2018

日志相关概念

日志是一种可以追踪某些软件运行时所发生事件的方法。软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情。一个事件可以用一个可包含可选变量数据的消息来描述。此外,事件也有重要性的概念,这个重要性也可以被称为严重性级别(level)。

日志的作用

通过log的分析,可以方便用户了解系统或软件、应用的运行情况;如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。

简单来讲就是,我们通过记录和分析日志可以了解一个系统或软件程序运行情况是否正常,也可以在应用程序出现故障时快速定位问题。比如,做运维的同学,在接收到报警或各种问题反馈后,进行问题排查时通常都会先去看各种日志,大部分问题都可以在日志中找到答案。再比如,做开发的同学,可以通过IDE控制台上输出的各种日志进行程序调试。对于运维老司机或者有经验的开发人员,可以快速的通过日志定位到问题的根源。可见,日志的重要性不可小觑。日志的作用可以简单总结为以下3点:

  • 程序调试
  • 了解软件程序运行情况,是否正常
  • 软件程序运行故障分析与问题定位

如果应用的日志信息足够详细和丰富,还可以用来做用户行为分析,如:分析用户的操作行为、类型洗好、地域分布以及其它更多的信息,由此可以实现改进业务、提高商业利益。

发现问题

最近在用Python的logging模块记录日志时,遇到了重复记录日志的问题,第一条记录写一次,第二条记录写两次,第三条记录写三次。。。很头疼,这样记日志可不行。网上搜索到了原因与解决方案:

原因:没有移除handler

解决:在日志记录完之后removeHandler

修改前示例代码:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)

 logger.addHandler(streamhandler)
 logger.error(message)

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

修改前输出结果:

2016-07-08 09:17:29,740 - ERROR - testlog - hi
2016-07-08 09:17:29,740 - ERROR - testlog - hi too
2016-07-08 09:17:29,740 - ERROR - testlog - hi too
2016-07-08 09:17:29,740 - ERROR - testlog - hi three
2016-07-08 09:17:29,740 - ERROR - testlog - hi three
2016-07-08 09:17:29,740 - ERROR - testlog - hi three

修改后示例代码:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)

 logger.addHandler(streamhandler)
 logger.error(message)

 # 添加下面一句,在记录日志之后移除句柄
 logger.removeHandler(streamhandler)

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

修改后输出结果:

2016-07-08 09:32:28,206 - ERROR - testlog - hi
2016-07-08 09:32:28,206 - ERROR - testlog - hi too
2016-07-08 09:32:28,206 - ERROR - testlog - hi three

深度解析:

Google之后,大概搞明白了,就是你第二次调用log的时候,根据getLogger(name)里的name获取同一个logger,而这个logger里已经有了第一次你添加的handler,第二次调用又添加了一个handler,所以,这个logger里有了两个同样的handler,以此类推,调用几次就会有几个handler。。

所以这里有以下几个解决办法:

  • 每次创建不同name的logger,每次都是新logger,不会有添加多个handler的问题。(ps:这个办法太笨,不过我之前就是这么干的。。)
  • 像上面一样每次记录完日志之后,调用removeHandler()把这个logger里的handler移除掉。
  • 在log方法里做判断,如果这个logger已有handler,则不再添加handler。
  • 与方法2一样,不过把用pop把logger的handler列表中的handler移除。

下面是方法3与方法4的代码示例:

方法3:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 # 这里进行判断,如果logger.handlers列表为空,则添加,否则,直接去写日志
 if not logger.handlers:
 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)
 logger.addHandler(streamhandler)

 logger.error(message)

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

方法4:

import logging

def log(message):
 logger = logging.getLogger('testlog')

 streamhandler = logging.StreamHandler()
 streamhandler.setLevel(logging.ERROR)
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 streamhandler.setFormatter(formatter)

 logger.addHandler(streamhandler)

 logger.error(message)

 # 用pop方法把logger.handlers列表中的handler移除,注意如果你add了多个handler,这里需多次pop,或者可以直接为handlers列表赋空值
 logger.handlers.pop()
 # logger.handler = []

if __name__ == '__main__':
 log('hi')
 log('hi too')
 log('hi three')

这几种方法都亲试可行,个人觉得方法3判断更加优雅,你觉得呢?

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python多重继承新算法C3介绍
Sep 28 Python
详解使用Python处理文件目录的相关方法
Oct 16 Python
Python编程中对文件和存储器的读写示例
Jan 25 Python
Python实现连接postgresql数据库的方法分析
Dec 27 Python
Tornado Web Server框架编写简易Python服务器
Jul 28 Python
python通过配置文件共享全局变量的实例
Jan 11 Python
详解pandas删除缺失数据(pd.dropna()方法)
Jun 25 Python
对Python3中列表乘以某一个数的示例详解
Jul 20 Python
解决python3 安装不了PIL的问题
Aug 16 Python
python 制作本地应用搜索工具
Feb 27 Python
python re模块和正则表达式
Mar 24 Python
Python+OpenCV实现在图像上绘制矩形
Mar 21 Python
python 读写文件,按行修改文件的方法
Jul 12 #Python
Python实现的网页截图功能【PyQt4与selenium组件】
Jul 12 #Python
python基础学习之如何对元组各个元素进行命名详解
Jul 12 #Python
详解Python中的分组函数groupby和itertools)
Jul 11 #Python
Python中的groupby分组功能的实例代码
Jul 11 #Python
python中实现字符串翻转的方法
Jul 11 #Python
Python3.7中安装openCV库的方法
Jul 11 #Python
You might like
PHP文件大小格式化函数合集
2014/03/10 PHP
PHP生成RSS文件类实例
2014/12/05 PHP
YII2.0之Activeform表单组件用法实例
2016/01/09 PHP
php进程daemon化的正确实现方法
2018/09/06 PHP
学习ExtJS(一) 之基础前提
2009/10/07 Javascript
关于Aptana Studio生成自动备份文件的解决办法
2009/12/23 Javascript
js 代码优化点滴记录
2012/02/19 Javascript
js的匿名函数使用介绍
2013/12/11 Javascript
javascript省市级联功能实现方法实例详解
2015/10/20 Javascript
基于javascript实现全屏漂浮广告
2016/03/31 Javascript
JS中mouseover和mouseout多次触发问题如何解决
2016/06/06 Javascript
jQuery 的 ready()的纯js替代方法
2016/11/20 Javascript
详解使用angular-cli发布i18n多国语言Angular应用
2017/05/20 Javascript
AngularJS入门教程一:路由用法初探
2017/05/27 Javascript
vue中设置height:100%无效的问题及解决方法
2018/07/27 Javascript
详解微信图片防盗链“此图片来自微信公众平台 未经允许不得引用”的解决方案
2019/04/04 Javascript
npx create-react-app xxx创建项目报错的解决办法
2020/02/17 Javascript
全网小程序接口请求封装实例代码
2020/11/06 Javascript
[01:04:32]DOTA2-DPC中国联赛 正赛 Aster vs LBZS BO3 第二场 2月23日
2021/03/11 DOTA
优化Python代码使其加快作用域内的查找
2015/03/30 Python
Python内置的HTTP协议服务器SimpleHTTPServer使用指南
2016/03/30 Python
Python+selenium实现截图图片并保存截取的图片
2018/01/05 Python
python实现画圆功能
2018/01/25 Python
TensorFlow深度学习之卷积神经网络CNN
2018/03/09 Python
Python中函数的返回值示例浅析
2019/08/28 Python
Python实现线性插值和三次样条插值的示例代码
2019/11/13 Python
python NumPy ndarray二维数组 按照行列求平均实例
2019/11/26 Python
基于Html5实现的react拖拽排序组件示例
2018/08/13 HTML / CSS
html5 offlline 缓存使用示例
2013/06/24 HTML / CSS
美国市场上最实惠的送餐服务:Dinnerly
2018/03/18 全球购物
Europcar德国:全球汽车租赁领域的领导者
2018/08/15 全球购物
Java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
2015/08/04 面试题
目标责任书范本
2014/04/16 职场文书
环保倡议书500字
2014/05/15 职场文书
2014年生活老师工作总结
2014/12/23 职场文书
小升初自荐信范文
2015/03/05 职场文书