前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >是谁偷偷动了我的 logger

是谁偷偷动了我的 logger

作者头像
OpenMMLab 官方账号
发布于 2022-04-09 08:39:32
发布于 2022-04-09 08:39:32
65410
代码可运行
举报
文章被收录于专栏:OpenMMLabOpenMMLab
运行总次数:0
代码可运行

新创建的 logger 为何无法正确 “发声”,不怀好意的日志究竟从何而来,精心配置的 logger 竟然然口口吐吐叠叠词词,到底是配置者的失误还是来自三方库的暗箱操作,欢迎走进 logging 详解的第一期:《是谁偷偷动了我的 logger》。

GOODGOOD,通过 mmcv 配置的 logger 日志格式整齐,还能将日志导出到文件,能够很好的备份实验数据,舒服~

诶,最近 torch 也更新了,赶快跟上的时代的步伐,给自己的训练任务升个级!

调试完成,实验启动!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2022-01-28 15:01:45,436 - mmdet - INFO - Epoch [1][0/7330]        lr: 1.000e-02, eta: 8:28:55, time: 0.248, data_time: 0.008, memory: 3510, loss_cls: 0.9523, loss_bbox: 0.6255, loss: 1.5778
Epoch [1][0/7330]        lr: 1.000e-02, eta: 8:28:55, time: 0.248, data_time: 0.008, memory: 3510, loss_cls: 0.9523, loss_bbox: 0.6255, loss: 1.5778
Epoch [1][0/7330]        lr: 1.000e-02, eta: 8:28:55, time: 0.248, data_time: 0.008, memory: 3510, loss_cls: 0.9523, loss_bbox: 0.6255, loss: 1.5778
Epoch [1][0/7330]        lr: 1.000e-02, eta: 8:28:55, time: 0.248, data_time: 0.008, memory: 3510, loss_cls: 0.9523, loss_bbox: 0.6255, loss: 1.5778
...

纳尼,日志怎么影分身了,几张卡就有几次重复的日志,不应该啊,这难道就是卡多的烦恼?我 mmcv 版本也没更新,只是更新了 torch,按理说 logger 都是一样的配方,为啥日志乱了呢?

为了解决多重日志的 bug,我追根溯源的对 python 的 logging 模块进行了地毯式的 debug。在我第 10086 次调试过程中,出现了诡异的现象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> # import “三方”库
>>> logger = logging.getLogger("misery debug")
>>> logger.setLevel(logging.INFO)
>>> logger.info("问题究竟在哪")
misery debug 现在的年轻人难道希望问题很好定位,bug 很好修,程序一跑就 ok?不会吧?

我的日志 "问题究竟在哪" 为什么没有显示?这句 "misery debug 现在的年轻人难道希望问题很好定位,bug 很好修,程序一跑就 ok?不会吧?" 又是从哪来的?到底是谁对我的 logger 动了手脚?

问题一:我的 logger 去哪了

上例中,我明明想输出 “问题究竟在哪”,但是为什么这条日志没有在终端出现呢?事实上,使用 logging .getLogger 接口获取的 logger,只是一个胚胎,本身不具备任何输出日志的能力。logger 是通过自身携带的 handler 来输出日志的,例如 StreamHandler(向终端输出日志)和 FileHandler(向文件输出日志)。所以我们需要为创建的 logger 配置 handler,从而能够实现输出日志的功能。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> logger = logging.getLogger("misery debug")
>>> logger.setLevel(logging.INFO)
>>> logger.handlers.addHandler(logging.StreamHandler())
>>> logger.info("问题究竟在哪")
问题究竟在哪
misery debug 现在的年轻人难道希望问题很好定位,bug 很好修,程序一跑就 ok,不会吧?

在为 logger 配置上 StreamHandler 后,日志就能正常输出了。

问题二:是谁在默默低语

misery debug 现在的年轻人难道希望问题很好定位,bug 很好修,程序一跑就 ok,不会吧?

这句嘲讽的打印和 mmcv 日志多重打印其实是一回事,而 logging 库的继承关系则是这一系列问题的症结所在。

众所可能不周知, logging 库为了简化配置 logger 的流程,允许我们通过派生“子实例”来获取日志格式和父实例一样的 logger。为了便于介绍日志格式的继承关系,下例中我们直接使用 mmcv.get_logger 函数来获取 logger(get_logger 接口能够获取有统一日志格式的 logger)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> parent_logger = mmcv.get_logger('parent')
>>> child_logger = parent_logger.getChild('child')
>>> child_logger.info("I'm child")
2022-03-01 21:58:18,438 - parent.child - INFO - I'm child

logging.Logger.getChild() 方法能够非常简单的获取具有相同日志格式的子实例,但是这也是多重日志的根源。

鲁迅曾经说过,人类拥有共同的祖先,logger 也不例外,那就是 logging.root 。所有通过 logging.getLogger 方法创建的实例,都拥有共同的祖先:logging.root。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> logger = logging.getLogger('roommate')
>>> logger = logger.getChild('roommate')
>>> logger = logger.getChild('roommate')
>>> logger = logger.getChild('roommate')
>>> while logger.parent:
>>>     print(logger.parent.name)
>>>     logger = logger.parent
roommate.roommate.roommate
roommate.roommate
roommate
root

这样就会引入一个问题,如果三方库配置了 logging.root ,按照继承逻辑是否会影响到我们的日志呢,答案是会的。我仔细检查了我导入的模块(自导自演),终于找到了 devil_whisper.py

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import logging
handler = logging.StreamHandler()
format = logging.Formatter("%(name)s 现在的年轻人难道希望问题很好定位,bug 很好修,程序一跑就 ok,不会吧?")
handler.setFormatter(format)
handler.setLevel(logging.INFO)
logging.root.addHandler(handler)

由于 logging.getLogger("misery debug") 获取的 logger 继承自 logging.root,因此就会输出 misery debug 现在的年轻人难道希望问题很好定位,bug 很好修,程序一跑就 ok,不会吧?

mmcv 出现多重日志的原因也是如此。尽管 mmcv 的 mmcv.get_logger 会将 logging.root 的 handler 的日志等级设置成 ERROR,从而过滤所有 ERROR 等级以下的日志,但是如果三方库在 mmcv.get_logger 之后再配置 logging.root,就会出现多重日志的现象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/logging.py
>>> def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'):
>>>     ...
>>>     for handler in logger.root.handlers:
>>>         if type(handler) is logging.StreamHandler:
>>>             handler.setLevel(logging.ERROR)
>>>     ...
>>>    return logger
    
>>> logging.root.addHandler(logging.StreamHandler())
>>> logger = get_logger('mmcv')  # 将 Handler 的日志等级设置为 ERROR.
>>> logger.info('不会触发多重日志!')
2022-03-01 22:16:42,682 - mmcv - INFO - 不会触发多重日志!
>>> logging.root.addHandler(logging.StreamHandler())  # root 新增了 handler.
>>> logger.info('触发多重日志!')
2022-03-01 22:16:42,682 - mmcv - INFO - 触发多重日志!
触发多重日志!

既然继承是多重日志的万恶之源,我们就需要理解 logging.Logger 的继承逻辑,这边以流程图的形式展示 handler 的继承规则。

Handler 的继承规则

不难发现,logging 模块通过 propagate 属性实现了 handler 的 “继承”。当 propagate 为 True 时,子实例会向上搜索并调用父实例的 Handler 从而触发多重打印。我们只需要另 propagate 为 False,能走出万恶的继承循环了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> logging.root.addHandler(logging.StreamHandler())
>>> logger = mmcv.get_logger('mmcv')
>>> logger.propagate = False
>>> logger.info('不会触发多重日志!')
2022-03-01 22:31:03,429 - mmcv - INFO - 不会触发多重日志!
>>> logging.root.addHandler(logging.StreamHandler())
>>> logger.info('触发多重日志!')
2022-03-01 22:31:03,429 - mmcv - INFO - 触发多重日志!

可以看到两句话都只被打印了一遍,问题成功解决。

虽然找到了多重日志的根源,但这只是 logging 模块的冰山一角。如何配置会 “察言观色” 的 logger,能够按照日志等级输出不同格式的日志,如何缕清 logger 父子实例复杂的继承关系?尽情期待第二期:《三句话,让 logger 对我言听计从》~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 OpenMMLab 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
怎么联系你
怎么联系你
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
TienChin 活动管理-添加活动页面
直接将原有的 index.vue 的全部内容替换成下面的,这里先替换,我只是补齐文档,后面新模块开发我会一步一步进行记录起来:
程序员NEO
2023/10/12
6650
猿实战08——属性库实现之属性关系绑定
属性和属性值,看上去很不起眼,数据粒度也很小,但是正式因为数据粒度小,灵活多变,组织得当可以强有力的区分千变万化的商品。
山旮旯的胖子
2020/09/05
8800
猿实战08——属性库实现之属性关系绑定
TienChin 活动管理-活动列表展示
程序员NEO
2023/10/12
5510
TienChin 活动管理-活动列表展示
vue2.0+Element-ui实战案例
我们将会选择使用一些 vue 周边的库vue-cli, vue-router,axios,moment,Element-ui搭建一个前端项目案例,后端数据接口,会使用json-server快速搭建一个本地的服务,方便对数据的增删改查,
小周sir
2019/09/23
2.3K0
vue2.0+Element-ui实战案例
基于HTML+CSS+JavaScript角色后台管理系统设计毕业论文源码
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师
IT司马青衫
2022/08/16
1.2K0
基于HTML+CSS+JavaScript角色后台管理系统设计毕业论文源码
ElementUI Dialog 对话框,组件之间传值
Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下面是应用了 Element Table 和 Form 组件的两个样例。
py3study
2021/01/29
4.9K0
手把手教你实现一个Vue无限级联树形表格(增删改)
平时我们可能在做项目时,会遇到一个业务逻辑。实现一个无限级联树形表格,什么叫做无限级联树形表格呢?就是下图所展示的内容,有一个祖元素,然后下面可能有很多子孙元素,你可以实现添加、编辑、删除这样几个功能。
马克社区
2022/05/11
1.6K0
el-table 多表格弹窗嵌套数据显示异常错乱问题
使用vue+element开发报表功能时,需要列表上某列的超链接按钮弹窗展示,在弹窗的el-table列表某列中再次使用超链接按钮点开弹窗,以此类推多表格弹窗嵌套,本文以弹窗两次为例 最终效果如下示例页面
GoodTime
2024/03/05
3230
el-table 多表格弹窗嵌套数据显示异常错乱问题
vue的修饰符!sync和el-dialog弹窗组件
父组件 index.vue: <template> <info :value="myValue" @valueChanged="e => myValue = e"></info> </template> <script> inport info from './info.vue'; export default { components: { info, }, data() { return {
leader755
2022/03/09
7830
微服务项目:尚融宝(42)(核心业务流程:借款额度审批(2))
创建 src/views/core/borrow-info/detail.vue 
一个风轻云淡
2022/11/15
3870
微服务项目:尚融宝(42)(核心业务流程:借款额度审批(2))
实现表格行的拖拽以及分页
在做一些后台管理系统时,表格的数据信息展示是很常见的需求,而对应的都是一些增删改查的操作
itclanCoder
2021/12/06
3.1K0
实现表格行的拖拽以及分页
猿实战13——实现你没听说过的前台类目
上几个章节,猿人君教会了你实现了属性/属性值和后台类目的绑定关系,今天,猿人君就带你来实现前台类目。
山旮旯的胖子
2020/09/23
6720
猿实战13——实现你没听说过的前台类目
Vue电商实践项目(二)
1.实现后台首页的基本布局 2.实现左侧菜单栏 3.实现用户列表展示 4.实现添加用户
用户6808043
2022/02/24
5.1K0
vue表格分页以及增删改查的实际应用
效果 1:表格以及分页 2:增加一条数据 3:删除一条数据 4:修改一条数据 5:查询一条数据 实例: <template> <div class="tab-container"> <d
王小婷
2021/03/17
1.9K0
TienChin-课程管理-添加课程页面
这个 index.vue 是 course 文件夹下面的 index.vue 别弄错了。
程序员NEO
2023/10/12
2200
Vue + Element ui 实现动态表单,包括新增行/删除行/动态表单验证/提交功能
最近通过Vue + Element ui实现了动态表单功能,该功能还包括了动态表单新增行、删除行、动态表单验证、动态表单提交功能,趁热打铁,将开发心得记录下来,方便以后再遇到类似功能时,直接拿来应用。
朱季谦
2023/07/21
6.4K0
Vue + Element ui 实现动态表单,包括新增行/删除行/动态表单验证/提交功能
vue3+element-plus 表格table实现树状结构父子级互不影响
用户5899361
2024/01/31
1.1K0
element ui 图片上传封装多张或单张
最近写了一个后台管理项目,发现每个后台项目都离不开上传图片,决定把上传图片做个封装,话不多说直接上代码!
前端小白@阿强
2020/08/11
2.4K0
手把手教你实现一个Vue无限级联树形表格(增删改)
平时我们可能在做项目时,会遇到一个业务逻辑。实现一个无限级联树形表格,什么叫做无限级联树形表格呢?就是下图所展示的内容,有一个祖元素,然后下面可能有很多子孙元素,你可以实现添加、编辑、删除这样几个功能。
Vam的金豆之路
2021/12/01
5880
手把手教你实现一个Vue无限级联树形表格(增删改)
不需要web服务器,如何构建一个可以内部跨域的http服务(Vue+Flask)
前端把需要测试的接口地址,报文通过axios 发送给后端Flask服务,Flask服务通过 requests 模块实现测试
山河已无恙
2023/03/02
8560
推荐阅读
相关推荐
TienChin 活动管理-添加活动页面
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验