创作背景
最近本菜鸡在使用 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
的小伙伴可以 私信我
进群哦。
以上就是我要分享的内容,因为 学识尚浅,会有不足,还 请各位大佬指正。
有什么问题也可在评论区留言。