社区编辑申请
注册/登录
为什么Pycharm输出的日志全部是红色!
开发 后端
如果未在记录器上显式设置级别,则使用其父记录器的级别作为其有效级别。如果父记录器也没有设置级别,则依此类推,搜索父级的父级,直到找到明确设置了级别的记录器。

在上一篇文章1万字详解 python logging日志模块 中,深入浅出的讲解了日志的基本原理与用法。但还有一些内容并没有涉及到,所以这篇文章作为上一篇文章的补充。

希望这两篇文章能帮助你完全理解日志模块的使用,在项目中对日志的运用游刃有余。上一篇还没看的建议先阅读上一篇

1、为什么子记录器不需要设置日志等级也可以输出?

如果未在记录器上显式设置级别,则使用其父记录器的级别作为其有效级别。如果父记录器也没有设置级别,则依此类推,搜索父级的父级,直到找到明确设置了级别的记录器。根记录器默认下为 WARNING 级别

  1. import logging 
  2.  
  3. parent = logging.getLogger("parent"
  4. parent.setLevel(logging.INFO) 
  5. parent.addHandler(logging.StreamHandler()) 
  6.  
  7. child = logging.getLogger("parent.child"
  8. child.info("msg"

输出

  1. msg 

这里我没有给child设置日志等级,他会从父记录器查找日志级别,所以child也可以输出info级别的日志。关于记录器的继承关系可以参考第一篇文章

2、为什么有时候日志会输出两次?

看下面例子:

  1. import logging 
  2.  
  3. # 初始化日志,并设置日志级别(为root设置为DEBUG级别,关联StreamHandler,设置BASIC_FORMAT格式) 
  4. logging.basicConfig(level=logging.DEBUG) 
  5.  
  6. # 定义root记录器 
  7. root = logging.getLogger() 
  8.  
  9. # 定义child记录器 
  10. child = logging.getLogger("child"
  11. console_handler = logging.StreamHandler() 
  12. # 给child绑定处理器 
  13. child.addHandler(console_handler) 
  14. # 记录一条info日志 
  15. child.info("child info"

输出

  1. child info 
  2. INFO:child:child info 

代码中明明只记录了一次日志,却输出了两次,而且两次的日志格式不一样。这是因为 child 这个记录器添加了一个叫console_handler的处理器, 而root根记录器默认也带有自己的处理器(也是StreamHandler实例)

  1. print(root.handlers) # [<StreamHandler <stderr> (NOTSET)>] 

根据python中日志模块的处理机制,子记录器记录的消息会自动传播给父级记录器的关联的处理器。所以在这个例子中,child记录的消息除了会发给自己的handler外,还是传播给root记录器的handler,因此最终输出了两次,流程图如下

logging-flow.png

如果不希望子记录器记录的消息传播给父级记录器,可以设置记录器的属性propagate为False,关闭传播。

  1. child.propagate = False 

如此一来,最终输出到终端的日志就只有child自己的处理器输出的记录

  1. child info 

配置处理器的最佳实践是给顶级记录器配置处理器,再根据需要创建子记录器, 因为记录最终都会传播给父记录器

  1. import logging 
  2.  
  3. parent = logging.getLogger("parent"
  4. parent.setLevel(logging.DEBUG) 
  5. parent.addHandler(logging.StreamHandler()) 
  6.  
  7. # 不需要给子记录器单独配置handler 
  8. child = logging.getLogger("parent.child"
  9. child.info("msg"

对于日志的流程处理,python官方文档画了一张更为细致的流程图,可以参考

logging_flow.png

第一次看估计有点晕,但先看我画的这张图再来看这张图,你就能懂了,为了简化我省去了过滤器以及不断循环查找父级记录器的这个流程。

3、 为什么我的pycharm中输出的日志是红色?

不知道你的pycharm输出的日志不管是info信息还是error信息,反正都是红色,一看以为整屏都是错误。

把下面代码放在Pycharm运行看效果:

  1. import logging 
  2. logging.basicConfig(level=logging.DEBUG) 
  3. logging.info("hello"

这是因为使用root记录器记录日志时,默认配置的handler是一个StreamHandler。

我们打开StreamHandler的源码

  1. class StreamHandler(Handler): 
  2.     ""
  3.     A handler class which writes logging records, appropriately formatted, 
  4.     to a stream. Note that this class does not close the stream, as 
  5.     sys.stdout or sys.stderr may be used. 
  6.     ""
  7.  
  8.     terminator = '\n' 
  9.  
  10.     def __init__(self, stream=None): 
  11.         ""
  12.         Initialize the handler. 
  13.  
  14.         If stream is not specified, sys.stderr is used. 
  15.         ""
  16.         Handler.__init__(self) 
  17.         if stream is None: 
  18.             stream = sys.stderr 
  19.         self.stream = stream 

初始化这个Handler时,会接收一个stream的参数,如果不传,默认就使用的系统标准错误流(sys.stderr)输出,pycharm对错误流输出的字体样式做了红色渲染,如果换成 sys.stdout 输出的就不再红色了。

  1. import logging 
  2. import sys 
  3.  
  4. handler = logging.StreamHandler(stream=sys.stdout) 
  5. logging.basicConfig(level=logging.DEBUG, handlers=[handler]) 
  6.  
  7. # 或者指定stream参数 
  8. # logging.basicConfig(level=logging.DEBUG, stream=sys.stdout) 
  9.  
  10. logging.info("hello"

4、怎么生成以日期时间命名的日志?

实际应用中,我们会对日志进行归档存储,每天生成一份日志,如果哪天出了问题,也方便定位,直接找到当天的日志文件就可以分析。我们只需要给logger添加一个TimedRotatingFileHandler处理器就行。

  1. file_handler = TimedRotatingFileHandler(‘'logs/api.log'), 
  2.                                             when="D", interval=1, backupCount=10, 
  3.                                             encoding="UTF-8", delay=False, utc=True
  4. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  5. file_handler.setFormatter(formatter) 
  6. file_handler.setLevel(logging.INFO) 
  7. logger.addHandler(file_handler) 

5、为什么我日志配置后不生效?

可能跟程序的加载顺序有关,看个例子

  1. import logging 
  2.  
  3. logger = logging.getLogger() 
  4. handler = logging.StreamHandler() 
  5. logger.addHandler(handler) 
  6. logger.info("hello1"
  7.  
  8. logger.setLevel(logging.INFO) 
  9. logger.info("hello2"

像上面的代码最后只输出了hello2,不过实际场景中,代码没这么简单,通常是在a模块中的某个函数中初始化日志框架配置, 在b模块外层创建了名字叫 logger_b的记录器,然后在a中导入b模块时,这时候日志配置还没初始化,最后导致logger_b的配置就成了默认配置。所以有可能出现日志不生效的情况。

 

因此最佳实践是能尽早初始化日志配置就尽早提前。

 

责任编辑:武晓燕 来源: Python之禅
相关推荐

2022-04-06 22:26:14

Python工具PyCharm

2022-02-24 10:48:01

Pycharm插件

2022-05-07 10:09:01

开发Java日志

2022-04-11 09:15:44

中间件开源

2022-05-11 08:23:54

自动化测试软件测试

2022-05-12 08:22:54

内核oopsPC

2022-04-08 08:40:36

Nginx日志服务器

2022-04-22 12:49:50

Lite XL编辑器文本编辑器

2022-03-30 13:56:05

前端监控搭建

2022-04-15 09:23:29

Kubernetes面试题

2022-05-02 18:29:35

bashshellLinux

2022-04-22 11:16:35

Linux工具命令

2022-05-08 09:24:08

微软Windows 11

2022-04-05 13:46:21

日志数据库系统

2022-04-26 09:44:03

group byExtraMySQL

2022-03-18 21:27:36

Python无代码

2022-04-26 10:11:16

开发工具JavaScript

2022-04-14 09:30:22

深度学习激活函数人工神经

2022-04-02 10:23:12

MySQL数据库

2022-03-21 09:52:44

LinuxSystemd日志

同话题下的热门内容

Python 字符串总结,建议收藏!这份Java日志格式规范,拿走不谢!Mybatis-Plus官方发布分库分表神器,一个依赖轻松搞定!后端思维篇:如何应用设计模式优化代码改变 Python 对象规则的黑魔法 Metaclass几种限流算法的Go语言实现JMeter关联之正则表达式提取器在 Go 中实现一个支持并发的 TCP 服务端

编辑推荐

使用Kotlin做开发一个月后的感想面试官问你什么是消息队列?把这篇甩给他!五大自动化测试的Python框架图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)2018年最流行的十大编程语言,其中包括你用的语言吗?
我收藏的内容
点赞
收藏

51CTO技术栈公众号