python打印log重复问题
本博客转载于:http://www.cnblogs.com/huang-yc/p/9209096.html,写得真不错
浅析python日志重复输出问题
浅析python日志重复输出问题
问题起源:
在学习了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 logger
my_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 logging
def 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 logger
my_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
logger.handlers最初是一个空列表,执行‘logger.addHandler(ch)’添加一个‘StreamHandler’,输出一条日志- 在第二次被调用时,
logger.handlers已经存在一个‘StreamHandler’,再次执行‘logger.addHandler(ch)’就会再次添加一个‘StreamHandler’,此时的logger有两个‘StreamHandler’,输出两条重复的日志 - 在第三次被调用时,
logger.handlers已经存在两个‘StreamHandler’,再次执行‘logger.addHandler(ch)’就会再次添加一个,此时的logger有三个‘StreamHandler’,输出三条重复的日志
解决办法
1.改名换姓
import logging
# 为日志函数添加一个name,每次调用时传入不同的日志名
def my_log(name):
logger = logging.getLogger(name)
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 logger
my_log('log1').error('run one')
my_log('log2').error('run two')
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)
import logging
def my_log():
logger = logging.getLogger('mysql.log')
# 每次被调用后,清空已经存在handler
logger.handlers.clear()
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 logger
my_log().error('run one')
my_log().error('run two')
my_log().error('run three')
ps:removeHandler方法(兼容性较差)
# 这种写法下的可以使用removeHandler方法(logger.handlers.clear也可以使用在这种写法的函数内)
import logging
def my_log(msg):
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)
logger.error(msg)
# 在使用完ch后从移除Handler
logger.removeHandler(ch)
my_log('run one')
my_log('run two')
my_log('run three')
3.用前判断
import logging
def my_log():
logger = logging.getLogger('mysql.log')
# 判断logger是否已经添加过handler,是则直接返回logger对象,否则执行handler设定以及addHandler(ch)
if not 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 logger
my_log().error('run one')
my_log().error('run two')
my_log().error('run three')
总结
第一次遇到日志重复输出问题,那时还没有学习到面向对象编程的内容,当时并没有真正理解logging模块。学习完面向对象编程后,回过头来再思考这些问题有了豁然开朗的感觉。
比如起初对logging.getLogger的实际原理不是很理解,在学习了面向对象编程中的hasattr、getattr、setattr这样一些方法后就恍然大悟了。所以诸君如果现在还是对logging模块不太理解,不妨先不纠结于这些细节,继续学下去。
知识面扩充后,曾经的一些难题自然就会迎刃而解:)
python打印log重复问题的更多相关文章
- python打印日志log
整理一个python打印日志的配置文件,是我喜欢的格式. # coding:utf-8 # 2019/11/7 09:19 # huihui # ref: import logging LOG_FOR ...
- iOS 设备屏幕上实时打印 Log 的小工具
需求 写这个小工具的想法,主要来源于很多团队都会用友盟.TalkingData 等第三方框架做自定义事件统计:不过统计代码加好之后,没有很好的方法来让测试工程师验证一下事件加上了没有,调用次数有没有重 ...
- python统计元素重复次数
python统计元素重复次数 # !/usr/bin/python3.4 # -*- coding: utf-8 -*- from collections import Counter arr = [ ...
- 封装一个简单好用的打印Log的工具类And快速开发系列 10个常用工具类
快速开发系列 10个常用工具类 http://blog.csdn.net/lmj623565791/article/details/38965311 ------------------------- ...
- Python打印格式化与字符串
关于Python打印格式化与字符串,比较全面的总结,希望对大家有帮助~ # -*- coding: cp936 -*- ''' 打印格式 ''' print "a" print & ...
- python打印表格式数据,留出正确的空格和段落星号或注释
python打印表格式数据,留出正确的空格,格式化打出 代码如下: def printPicnic(itemsDict,leftWidth,rightWidth): print('PICNIC ITE ...
- 解决华为手机不打印Log信息的问题
在之前安装了Android Studio后,发现了一个很苦恼的事情,就是在程序中的写Log语句,不能正常的在Logcat中打印出来,这对于解决程序bug真是一刀切断,让人无从下手,在各种尝试后,首先我 ...
- python 打印 emoji
python 打印 emoji 如需转发,请注明出处:小婷儿的python https://www.cnblogs.com/xxtalhr/p/10486506.html 一.Unicode字符集: ...
- python打印列表的下标和值的例子:
python打印列表的下标和值的例子: In [1]: list01=[1,4,5] In [10]: def funct01(ll): ....: for index,value in ...
随机推荐
- H5 27-优先级之important
27-优先级之important 我是段落 <!DOCTYPE html> <html lang="en"> <head> <meta c ...
- c++入门之类与内存
类作为c++编程的核心,自然我们十分关注其内存分配问题. 这里的这个主题中,我们关注了静态成员,new,delete.还有构造函数和析构函数. 先上代码: # include "iostre ...
- python_函数名的应用、闭包、装饰器
0.动态传参内容补充: 0.1 单纯运行如下函数不会报错. def func1(*args,**kwargs): pass func1() 0.2 *的魔性用法 * 在函数定义的时候,代表聚合. *在 ...
- Python_架构、同一台电脑上两个py文件通信、两台电脑如何通信、几十台电脑如何通信、更多电脑之间的通信、库、端口号
1.架构 C/S架构(鼻祖) C:client 客户端 S:server 服务器 早期使用的一种架构,目前的各种app使用的就是这种架构,它的表现形式就是拥有专门的app. B/S架构(隶属于C/ ...
- Java面试题详解三:比较器
一,Comparable和Comparator1.Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较.Comparable接口中只有一 ...
- bootstrap 弹窗或者提示框插件 bootstrap-growl 和bootstrap-notify
Bootstrap简单好用的页面右上角咆哮提示框 - daidaineteasy的专栏 - CSDN博客https://blog.csdn.net/daidaineteasy/article/deta ...
- 爱上linux 简单实现移动办公处理环境.
1. 这周一直在鼓捣linux上面的环境测试. 简单的将 我们的产品部署到了linux上面 详情见前面的 blog 2. 有时候下班了 或者是 在WC (科技园wc排队 说多了都是泪) 或者是眼睛不舒 ...
- 校园电商项目3(基于SSM)——配置Maven
步骤一:添加必要文件夹 先在src/main/resources下添加两个文件夹 接着在webapp文件夹下添加一个resources文件夹存放我们的静态网页内容 WEB-INF里的文件是不会被客户端 ...
- 在eclipse中spring的xml配置文件标签中class路径全限定名自动提示设置
这个自动提示其实很简单,没有网上说的那些要在help下的Install中输入网址来下载更新一堆东西那么复杂. 只需要打开Help — — >Eclipse Marketplace... 然后在该 ...
- /proc/diskstats
读取磁盘统计信息,如下所示: linux-HpdBKE:~ # cat /proc/diskstats sda sda1 sda2 dm- dm- dm- sda为整个硬盘的统计信息,sda1为第一个 ...