赞
踩
可以说,python自带的标准日志库logging,是每个写python的程序员从新手小白到进阶小白(哈哈)必须会的。本文将介绍三个典型的场景,让你理解,什么时候应该使用日志,以及用哪种方式使用日志。
新手时期的典型代码基本都是如下形式的:
import xxx
def func1(xx):
xxxx
print(...)
xxxx
def func2(xx):
xxxx
print(...)
xxxxx
...
if __name__ == '__main__':
func1(...) ... func2(...)
这样的程序的问题在我这里是以下几个:
修改很繁琐,比如想对不同的print进行区分,你就得往里面写一大堆区分的符号,常见的比如 -----val_1-----;一个小部分debug完了你又得上去删除,有时候你写多了你得到处找,到处去注释
如果涉及递归、循环等会导致大量打印的内容,并且你在终端,比如VScode的终端运行程序,可能都翻不到最顶上,丢失很多展示内容,并且可能只能看一次,你再运行别的程序或者关闭了这个想明天再看,就没了
为了解决问题1,一般是会使用logging去替代print。有什么好处呢?logging可以用INFO, WARNING, DEBUG, ERROR那些等级去控制,比如你找bug的时候你设置logging等级为比较低的Debug,等到你把你的问题解决完了,你不用到处去找你的logging语句把它们注释掉,你直接把原来的logging等级设置为更高一级的INFO,那些输出就统统失效了
print('...') -> logging.debug('...')
# 当logging的日志级别是debug的时候才会输出,高于debug等级时,不会输出
最基础的使用方式是直接使用logging.xxxx
这样的使用方式下,在主程序中通过 logging.basicConfig() 快速设置输出日志文件的路径、格式、等级等,再在任意位置用 logging.debug()/info()/… 代替 print,那么就会统一使用该配置进行日志输出
import logging logging.basicConfig(filename="test.log", filemode="w", format="%(asctime)s %(name)s:%(levelname)s:%(message)s", datefmt="%d-%M-%Y %H:%M:%S", level=logging.DEBUG) # logging.debug(xxx)中的内容为message,而我们设置的format就是message如何和其他的信息结合,比如我们想有记录日志的时间戳等 logging.debug('This is a debug message') logging.info('This is an info message') logging.warning('This is a warning message') logging.error('This is an error message') logging.critical('This is a critical message') # 这样会记录得到 23-04-09 12:10:32 root:DEBUG:This is a debug message 23-04-09 12:10:32 root:INFO:This is an info message 23-04-09 12:10:32 root:WARNING:This is a warning message 23-04-09 12:10:32 root:ERROR:This is an error message 23-04-09 12:10:32 root:CRITICAL:This is a critical message
但是用着用着就会发现,还存在一些问题:
前面说的,如果有递归和循环,那不同的函数里写的日志我想要区分开怎么办?-> 取不同的日志器的名字
不同的日志都写在一个文件里,就算前面写着不同的名字,比如上面的例子里日志器都叫root,还是不好看怎么办?比如说,我想要不同函数把不同的日志写在不同的文件里面,我运行一次程序,可以有一个简要版本的日志文件生成出来,还同时有一个细节版本生成出来,让我可以关注一些特定环节的细节
一个一个来
我们需要对logging了解得更深入一些。
上面直接使用logging进行的快速使用,本质上是依靠四大底层组件:
当然了,程序员是他们的牛马,大牛程序员负责编写好filter, formatter, handler这些专业人士,封装在logging库里面,并且提供一些改装他们的接口,调包侠程序员们就负责把这些专业人士调教改装,等到专业人士都准备好了,你再从应用程序的视角,仅仅和logger进行简单的沟通就好了
通常,我们可以准备一份配置文件logging.conf,不同的logger,有不同的format,等级等
[loggers] keys=root,simpleExample [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=DEBUG handlers=consoleHandler [logger_simpleExample] level=DEBUG handlers=consoleHandler qualname=simpleExample propagate=0 [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
然后在应用程序中,就可以在不同的函数里按需调取不同的logger。但是注意,logger是遵循单例模式的,也就是说,实际上在一个python解释器中只有一个最最底层的rootLogger,我们创造的那些叫不同的名字、用不同的格式,在我们看来好像他们是不同的,但是实际上他们都会最终利用rootLogger的能力,所以我们不能创造logger实例,比如logger_A = logger()
这样,我们只能使用logging.getLogger(name)
来取得一个已经存在的logger,但是我们给他不同的功能。知道就可以
使用时:
import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')
基本没有很大的改变,但是确实会让日志中输出的内容更加丰富,有的前面写的root: message balabala;有的前面写的 simpleExample: message balabala,并且可以具备详略得当的格式
因为是我遇到的一个场景,所以单独讲讲如何把不同日志输出到不同的日志文件中,本质上还是运用2中的知识。我的场景是这样的:我的主程序中有两次多进程相关的内容,一方面我需要记录主程序运行过程中,两次结果汇总是否正确,一方面我需要知道子进程的详细处理过程是否正确。因此就有这样的需求,把主程序的输出到一个文件,把子进程的输出到另外的几个文件里,这样看起来也方便,不会多个进程的输出混在一起。(如果有更好的做法还希望大佬们教教我呀,交流一下)
核心思想就是为一个logger准备它独有的handler,因为handler是负责发送日志message到具体文件的嘛,当然了,它也可以有独有的formatter那些,因为涉及格式
# 某个子进程函数
def worker(...):
specified_logger = logging.getLogger(log_name) # 取个logger名字
# 创建专有的handler, formatter等
handler = logging.FileHandler(file_name)
handler.setFormatter(specified_formatter)
# 传达给logger
specified_logger.setLevel(level)
specified_logger.addHandler(handler)
# 接下来就可以使用该logger输出到不同文件了
specified_logger.info('...')
有时候,我们希望每个函数独立默默地将自己的log输出到对应的文件就行了,不希望它们抢着一起输出到命令行中,此时对每个logger设置logger_name.propagate=False
就可以了,它们会抢着输出到命令行,是因为这些自定义的logger其实都是会把message回传到root_logger的,root_logger的handler中有命令行输出的streamHandler,所以就会输出到命令行。如果我们关掉logger的propagate,message就不会被root_logger获取
本篇是python中的一个小但是重要的知识点,也确实困扰过我一阵子,但是后来就会发现很多思想都是统一的。比如写python的人肯定也绕不开matplotlib
这个库,深入了解就会发现,它也是一个Artist对象做你的小秘,然后指导专业的Renderer负责专门的显色细节,在FigureCanvas(画布对象)上面画画。明白这一点以后,能够做出很多你需求定制化的更高级的事情,而不是被高级API束手束脚的。
如有错误,还请指正!拜托拜托!
(其实一直蛮害怕半桶水晃荡的时候分享自己的学习心得的,因为能想起学生时代,你和别人对答案,别人在你的答案的基础上,回去琢磨完发现你的答案错了,但是他不告诉你,第二天他对你错的场景,那真是有心理阴影哈哈哈)
But anyway, 做费曼学习法的践行者!必须有人先开始!
非常推荐阅读的官方文档,说得也很清楚,就是觉得进阶的示例还是少了一些
https://docs.python.org/3/howto/logging.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。