解决设置 logger 后 cmd 还是输出报错信息的问题
创作背景
最近本菜鸡在使用 logging 时想要进行如下设置:
- 命令行不输出日志信息
ERROR信息输出到error.log文件中- 其他都输出到
process.log文件中
但是我设置完 logger 后命令行还是有报错信息输出,这是怎么回事呢?
本文就教你如何解决这个问题,也是记录一下以供以后的我学习。
如果觉得我这篇文章写的好的话,能不能给我 点个赞 ,评论 、收藏 一条龙(☆▽☆)。如果要点个 关注 的话也不是不可以🤗。
请各位参加一下文末的 投票 哦,如果 有什么不足之处,还 请各位大佬在评论区提出,不胜感激。
问题代码
先来看看问题代码。
import logging
error_log_path = r'error.log'
major_log_path = r'process.log'
logging.basicConfig(
format='%(asctime)s | %(filename)s[line:%(lineno)d] | %(levelname)s | Message: %(message)s',
filemode='a'
)
major_logger = logging.getLogger()
major_logger.addHandler(logging.FileHandler(major_log_path))
exc_info_logger = logging.getLogger()
exc_info_logger.addHandler(logging.FileHandler(error_log_path))
def keep_log(message, level='info'):
message = str(message).replace('\n', '').replace('|', ' ')
if level.lower() == 'error':
exc_info_logger.error('', exc_info=True)
else:
f = eval(f'major_logger.{level.lower()}')
f(message)
其中:
major_logger实现功能3。exc_info_logger实现功能2。- 两个
logger共同实现功能1。
使用代码测试。
try:
a = 1 / 0
except:
keep_log('除数不能为零哦', 'error')
执行后确实会创建 error.log 文件,且写入信息,如下图所示。

可是出现了问题,如下图所示。

命令行也同样输出了错误信息。
这是怎么回事呢?
尝试解决
出现问题后,我向万能的百度求救,让我找到了一个看似可行的方案,链接 。

按照这位答主所说,需要将 propagate 属性设置为 False 才能达到我想要的效果。
尝试一下,代码如下:
major_logger = logging.getLogger()
major_logger.propagate = False
exc_info_logger = logging.getLogger()
exc_info_logger.propagate = False
但执行结果如下图所示:

命令行还是会输出错误结果。
如果 按照那位答主所说成功设置后能够隐藏命令行输出 并且 我的代码中 propagate 成功设置 的话,那问题应该是 Logger 中没有 propagate 这个属性导致功能没有实现。
查看一下 Logger 的源码:

有这个属性,如果成功设置的话应该会像答主所说的一样,那说明这个属性我没有成功设置。
问题出在哪里呢?
让我们看一下 getLogger 这个函数的源码:

其中有个 root ,看一下它的定义。
可以看到,root 是 RootLogger 的实例化对象,再看一下 RootLogger 的源码。
可以看到,RootLogger 是 Logger 的子类,追溯一下,可以找到 Logger 对象中进行 日志记录 的方法,如下图所示。

按照 getLogger 的逻辑,有 name 会 新建一个 Logger 对象 ,否则返回 root ,即 RootLogger(WARNING) 。
可以看到, callHandlers 函数中,如果 propagate 属性为 True 的话,就会调用该 logger 的父级 logger ,进行同样的输出。
但是,我的代码中两个 getLogger 都返回的 RootLogger ,也就是都对 RootLogger 进行设置。
可是既然都设置 RootLogger 了,为什么还是会在命令行输出呢?
让我回溯一下:
我设置了 baseConfig ,只有 format 和 filemode 两个属性,看一下 baseConfig 的源码:

当进行到画圈的位置时,因为没有制定日志文件路径,使用会实例化一个 StreamHandler 而不是 FileHandler 。
而且如下图所示,这个 StreamHandler 的输出是 sys.stderr 。

如下图所示,Logger 记录日志时会使用所有的 Handler 进行记录,其中就包括 StreamHandler ,所以会将日志输出至命令行。
为了验证我的猜想,在 getLogger 后输出 logger.handlers ,结果如下图所示。

和我的猜想一致。
你要问我为什么会多出来一个 process.log 的 FileHandler 和 error.log 的 FileHandler ,那我只能告诉你,我用的 jupyter ,直接重新运行的😜。
问题解决
既然已经知道了问题原因,那解决起来就挺简单了,只需要两步即可实现功能。
- 在
getLogger时传入参数name,这样返回的就是一个RootLogger的子Logger。 - 将此
logger的propagate属性设置为False,使其父Logger不会进行同等输出。
代码如下:
import logging
error_log_path = r'error.log'
major_log_path = r'process.log'
logging.basicConfig(
format='%(asctime)s | %(filename)s[line:%(lineno)d] | %(levelname)s | Message: %(message)s',
filemode='a'
)
major_logger = logging.getLogger('major')
major_logger.propagate = False
major_logger.addHandler(logging.FileHandler(major_log_path))
exc_info_logger = logging.getLogger('exc')
exc_info_logger.propagate = False
exc_info_logger.addHandler(logging.FileHandler(error_log_path))
def keep_log(message, level='info'):
message = str(message).replace('\n', '').replace('|', ' ')
if level.lower() == 'error':
exc_info_logger.error('', exc_info=True)
else:
f = eval(f'major_logger.{level.lower()}')
f(message)
再测试一下,结果如下图所示。

成功完成任务!!!
结尾
有想要一起学习 python 的小伙伴可以 私信我 进群哦。
以上就是我要分享的内容,因为 学识尚浅,会有不足,还 请各位大佬指正。
有什么问题也可在评论区留言。











