logger:一款管理日志的Python神器

article/2025/9/17 4:20:59

最近要新开一个项目,需要配个 logger 来管理日志,今天分享一下,喜欢记得点赞、关注、收藏。

【注】文末提供交流互助群

import logging
ori_logger = logging.getLogger('custom_logger')
ori_logger.setLevel(logging.INFO)
ori_logger.addHandler(logging.StreamHandler())
ori_logger.info('learn log')
# learn log

Emmm… 感觉日志丑丑的,NOT GOOD。为啥不试试 mmcv 的 get_logger 呢,功能齐全,一步到位。呐~链接拿去:

https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/logging.py

终端日志

mmcv_logger = get_logger('mmcv_logger', log_file='a.log')
mmcv_logger.info('learn log')
# 2022-03-24 10:38:35,998 - mmcv_logger - INFO - learn log

日志文件

2022-03-24 10:37:25,832 - mmcv_logger - INFO - learn log

太方便了!时间戳、日志名、日志等级,甚至还贴心地为你保存了日志文件,一行代码轻松搞定。

mmcv 的 get_logger 到底是是如何配置出如此便利的 logger 的呢?只需要配置 logger 三宝:日志等级(loglevel)、格式控制器(formatter) 和 日志处理器(handler),让 logger 对你言听计从。

logging 之日志等级****

logging 模块的日志等级如下:

CRITICAL = 50        
FATAL = CRITICAL
ERROR = 40           
WARNING = 30        
WARN = WARNING
INFO = 20            
DEBUG = 10           
NOTSET = 0          

Logging.Logger(本文中的 logger 为其实例)和 logging.Handler 的 level 属性,就表示对应实例的日志等级。当我们调用 logger.info(msg) 和 logger.warning(msg) 时,输出的消息也有日志等级。只有当消息的日志等级大于等于 logger.level 时,日志才有可能被输出。以我们之前提到的 logging.root 为例:

import logginglogger = logging.root
print(logger.level)  # 30, warning。root 日志等级为 WARNING (30)
logger.info("info msg")  # 日志等级为 INFO (20),小于 WARNING,无消息
logger.warning("warn msg")  # 日志等级为 WARNING (30),输出消息 warn msg
logger.error("erro msg")  # 日志等级为 ERROR (40),输出消息 erro msg
logger.setLevel(logging.ERROR) # 设置日志等级为 ERROR (40)
print(logger.level)  # 40, ERROR
logger.warning("warn msg")  # 无消息
logger.error("erro msg")  # erro msg

logging.root 的日志等级为 WARNING,因此无法输出 INFO 级别的消息。当我们把日志等级调整成 ERROR 后,则会无法输出 WARNING 级别的消息。但是奇怪的是,第一期中我们提到,logging.root 默认情况下是一个胚胎,本身不具备输出日志的能力,**为什么这里仍然能够输出 warning 和 error 级别的日志呢?**这里先留一个疑问,在 Handler 一节会给出答案。

logging 之 Handler****

既然 logging 模块是通过 Handler 来输出日志的,本节就先介绍 logging 模块的两大 Handler:streamHandler 和 fileHandler。

StreamHandler

向终端输出信息

配置了 StreamHandler 的 logger,可以和 print 一样向终端输出日志信息,示例如下:

logger = logging.root
# 创建 streamHandler
stream_handler = logging.StreamHandler()
# 设置日志等级
logger.setLevel(logging.INFO)
logger.info("learn logging")  # 没有配置 Handler,终端不会输出日志
# 为 root logger配置 Handler
logger.addHandler(stream_handler)
logger.info("learn logging")  #  配置 Handler,输出日志

向文件输出日志信息

实例化 StreamHandler 时,如果 stream 配置成写入的目标文件,就能将日志存储到文件中。

f = open('output.txt', 'w')
logger = logging.root
logger.setLevel(logging.INFO)
stream_handler = logging.StreamHandler(stream=f)
logger.addHandler(stream_handler)
logger.warning("learn logging")  # 此时 learn logging 会被写入到 output.txt 中
f.close()

FileHandler

FileHandler 继承自 StreamHandler,可以指定写入文件的编码格式,相比于 StreamHandler 更加灵活,易于使用:

logger = logging.root
logger.setLevel(logging.INFO)
# 设置输出文件和编码方式
file_handler = logging.FileHandler('output.txt', encoding='utf-8')
logger.addHandler(file_handler)
logger.info("WARNING")  # 此时 learn logging 会被写入到 output.txt 中

Handler 的日志等级

Handler 也有自己的日志等级。一条消息想经过 Handler 输出,需要同时满足消息的日志等级大于 logger 的日志等级和 handler 的日志等级。

图片

logger = logging.root
logger.setLevel(logging.DEBUG)  # logger 的日志等级为 DEBUG
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)  # handler 的日志等级为 INFO
logger.addHandler(handler)
logger.debug('learn logging')  # 不满足 handler,无法被输出
logger.info('learn logging')  # 同时满足 logger 含 handler,正常输出

logging 的暗箱操作

没有 handler 也能输出日志?

首先讲结论,logger 在没有 handler 的情况下,其本身是不具备输出消息能力的,streamHandler 的第一个例子已经说明了这个问题。但是为什么 logger.warning(msg) 和 logger.error(msg) 能够在不配置 handler 的情况下,输出日志呢?这其实是 logging 模块的保护机制,对于 warning 和 error 级别的消息,如果消息的日志等级大于 logger 的日志等级,且 logger 没有配置任何的 handler,则会调用 logging 模块内置的 streamHandler 来输出信息。

图片

为了证明这个逻辑,我们给 logger 配置一个 fileHandler,此时 logger.warning(msg) 就不会在终端输出日志了。

logger = logging.root
logger.warning("learn logging")  # 输出日志到终端
file_handler = logging.FileHandler('output.txt', encoding='utf-8')
logger.addHandler(file_handler)
logger.warning("learn logging")  # 不会输出日志到终端

坑爹的 logging.xxx

当你开心地调用 logging.info,logging.warning 时,你以为只是输出了一条日志,实际上可能给 logging.root 偷偷配置了 streamHandler。

logging.info('learn logging')
print(logging.root.handlers)
# [<StreamHandler <stderr> (NOTSET)>]

你可能会想,就多了个 streamHandler 嘛,这有啥。还记得被多重日志支配的恐怖么 。为什么使用 pytorch 1.10 突然出现了多重日志?追根溯源,是因为 pytorch 1.10 的 DistributedDataParallel 模块在 forward 过程中调用了 logging.info (https://github.com/pytorch/pytorch/blob/71f889c7d265b9636b93ede9d651c0a9c4bee191/torch/nn/parallel/distributed.py#L874),进而 logging.root 多出了一个 streamHandler(该过程发生在 mmcv.get_logger 之后,handler 的日志等级没有被设置成 ERROR),最终导致多重日志的发生。

logging 之 Formatter

如果说配置 Handler 相当于教 logger 说话,那么为 handler 配置 formatter 就相当于教 logger “优雅” 地说话。

给日志加上“主语”

我们可以通过配置 formatter,让 logger 输出的日志自带 logger 名。

logger = logging.root
handler = logging.StreamHandler()
handler.setFormatter(Formatter('%(name)s - %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info('learn logging')
# root - learn logging

这样输出的日志就会自带 “root” 日志名。

给日志加上时间

小学语文老师肯定教过,时间地点人物是七要素的前三甲,时间更是位居榜首,因此我们可以通过配置 formatter,让日志携带时间信息。

logger = logging.root
handler = logging.StreamHandler()
handler.setFormatter(Formatter('%(asctime)s - %(name)s - %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info('learn logging')
# 2022-03-22 01:36:42,900 - root - learn logging

给日志加上等级

日志等级可以凸显日志的重要性,例如我们会特别注意携带 ERROR 字段的日志。因此我们可以通过 formatter 让日志携带等级信息。

logger = logging.root
handler = logging.StreamHandler()
handler.setFormatter(Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info('learn logging')
# 2022-03-22 01:46:26,667 - root - INFO - learn logging

欸,有那味儿了,是不是和 OpenMMLab 系列的日志如出一辙?实际上 mmcv 配置 logger 的过程也是类似的,并且还有着更加全面的 handler 配置逻辑:mmcv/logging.py at master · open-mmlab/mmcv (github.com),大家可以参考借鉴哈。

小结

理解 loglevel、handler 和 formatter 的概念后,我们自己也可以动手写一个简易版的配置 logger 函数。

def custom_get_logger(name, out_file, log_level):# 设置日志名logger = logging.getLogger(name)# 设置日志登记logger.setLevel(log_level)# 设置格式formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s'' - %(message)s')# 配置输出日志到终端的 Handlerstream_handler = logging.StreamHandler()stream_handler.setFormatter(formatter)# 配置保存日志到文件的 Handlerfile_handler = logging.FileHandler(out_file)file_handler.setFormatter(formatter)# 添加 Handlerlogger.addHandler(file_handler)logger.addHandler(stream_handler)return loggercustom_logger = custom_get_logger('custom_logger', 'a.log', 'INFO')
custom_logger.info('learn log')
# 2022-03-24 11:18:52,120 - custom_logger - INFO - learn log

通过 custom_get_logger 接口获取的 logger,日志格式好看,也能存储到本地备份,基本和 mmcv 对齐。不过这样配置的 logger 还存在一些隐患,例如上期提到的多重日志。要想解决这些问题,我们不妨再回过头去看 mmcv.get_logger 的代码,相信经过这一期的学习,很多逻辑就变得容易理解。

至此 logging 模块的基本功能就介绍得差不多了,但是 logging 还存在一些隐式的暗箱操作,如果想彻底搞懂 logging 模块,敬请期待下一期的内容~

推荐文章

  • 李宏毅《机器学习》国语课程(2022)来了

  • 有人把吴恩达老师的机器学习和深度学习做成了中文版

  • 上瘾了,最近又给公司撸了一个可视化大屏(附源码)

  • 如此优雅,4款 Python 自动数据分析神器真香啊

  • 梳理半月有余,精心准备了17张知识思维导图,这次要讲清统计学

  • 年终汇总:20份可视化大屏模板,直接套用真香(文末附源码)

技术交流

欢迎转载、收藏、有所收获点赞支持一下!

在这里插入图片描述

目前开通了技术交流群,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友

  • 方式①、发送如下图片至微信,长按识别,后台回复:加群;
  • 方式②、添加微信号:dkl88191,备注:来自CSDN
  • 方式③、微信搜索公众号:Python学习与数据挖掘,后台回复:加群

长按关注


http://chatgpt.dhexx.cn/article/RqBAyaJ3.shtml

相关文章

Tensorboard + Logger 日志记录

在Pytorch 训练模型的时候&#xff0c;需要日志帮助开发者记录些重要信息和参数&#xff0c;以方便开发者更好的调节模型及参数&#xff0c;常见的日志非 Tensorboard不可&#xff0c;但是Pytorch 对 Tensorboard 的支持不是十分完美&#xff0c;在记录模型重要参数时 Tensorbo…

深入理解 rootLogger、logLogger、qtLogger

作者: 一去、二三里个人微信号: iwaleon微信公众号: 高效程序员 在使用 Log4Qt 时,你会发现有一系列的 logger - rootLogger()、logLogger()、qtLogger(),简直傻傻分不清楚! 为什么会有这么多 logger? 各 logger 之间有什么关系? 它们均适用于哪种场景? 参考文档对这部…

logger 报错

logger 报错&#xff1a;&#xff08;log4j 起不来&#xff0c;log message打不出来&#xff09; No appenders could be found for logger (com.vip.qa.android.base.DriverFactory). log4j:WARN Please initialize the log4j system properly. 原因&#xff1a; log4j.prop…

java的logger_java.util.logging.Logger 使用详解

概述: 第1部分 创建Logger对象 要使用J2SE的日志功能,首先要取得java.util.logging.Logger实例,这可以通过Logger类的两个静态getLogger()方法来取得: staticLogger getLogger(String name) 查找或创建一个logger。staticLogger getLogger(String name, String resourceBun…

Logger日志使用教程

Java util Logger的使用步骤 Java util Logger是java原生的日志生成工具&#xff0c;不需要另外引用类库&#xff0c;使用方便&#xff0c;学习简单&#xff0c;能够在小型应用中灵活使用。下面从实际应用角度&#xff0c;对Logger的使用步骤作出总结&#xff0c;以实现快速掌握…

Logger 日志管理

转载请注明出处&#xff1a; http://blog.csdn.net/like_program/article/details/52986553 1.Logger 是什么 在我们日常的开发中&#xff0c;肯定是少不了要和 Log 打交道&#xff0c;回想一下我们是怎么使用 Log 的&#xff1a;先定义一个静态常量 TAG&#xff0c;TAG 的值通…

【转】最详细的Log4J使用教程一、入门实例二、Log4J基本使用方法三、Spring中使用Log4J四、实战经验总结

原文地址&#xff1a;http://www.codeceo.com/article/log4j-usage.html 日志是应用软件中不可缺少的部分&#xff0c;Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。在apache网站&#xff1a;jakarta.apache.org/log4j可以免费下载到Log4j最新版本的软…

Logger打印日志

1. 一个最基本的例子 使用Logging框架写Log基本上就三个步骤 引入loggerg类和logger工厂类声明logger记录日志 下面看一个例子 // 1. 引入slf4j接口的Logger和LoggerFactory import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class UserService { // 2. 声明…

rename 批量修改文件名

1.批量修改文件后缀&#xff1a; rename s/// *.nii rename s/\.nii/\.txt/ * #把.nii后缀改为.txt rename s/\.txt// * #把.txt后缀去掉 rename s/$/\.nii/ * #加上后缀.nii rename s//\.nii/ * #这样会把.nii放到文件名前面&#xff0c;导致文件被隐藏。 rename s/^/dm/ * #…

Linux基本功系列之rename命令实战

文章目录 一. rename 命令介绍二. 语法格式及常用选项三. 参考案例3.1 将当前目录下所有.cfg的文件&#xff0c;替换为.txt结尾3.2 将所有出现mufeng的部分都替换为mufeng13.3 将mufeng0开头都变成mufeng00开头3.4 rename支持正则表示式 总结 前言&#x1f680;&#x1f680;&a…

VS项目rename

Visual Studio c项目更改相关文件名字 项目里面文件夹 Rename 右键 .sln文件, 打开方式–> txt方式打开编辑找到里面project的文件夹名字, Rename. 然后到资源管理器里, 找到对应的folder, Rename. .vcxproj相关文件 Rename 相关文件Rename .sln文件, txt打开–>Ren…

rename 命令 – 批量修改文件名称

rename 命令的功能是用于批量修改文件名称。与 mv 命令一次只能修改一个文件名不同&#xff0c;rename命令能够基于正则表达式对文件名进行批量修改&#xff0c;但要求是把匹配规则准确的描述给系统。 rename 命令的参数有三项&#xff1a;其一是当前文件名中要被修改的字符&am…

linux之rename命令

用字符串替换的方式批量改变文件名 rename 命令存在两个版本用法上有所区别 C语言版本, 支持通配符 [常用通配符说明] ? 表示一个任意字符 * 表示一个或一串任意字符 [charset] 可替代charset集中的任意单个字符Perl版本, 支持正则表达式 [常用正则表达式符号说明] ^…

pandas:案例详解 rename函数 修改列名和行名

pandas&#xff1a;案例详解rename函数 修改列名和索引 rename函数简介0 构建学习数据1 修改索引两种方式2 修改列名两种方式3 是否替换原列表3 pandas 字母转换大小写3 使用axis参数常见问题问题&#xff1a;参数位置错误修改办法&#xff1a; rename函数简介 df.rename(inde…

关于Unity 2020找不到PBR graph的问题,shader graph 10 版本

2020.4以后采用了船新的Shader Graph&#xff0c;变动挺大的。 建议还是换版本或者去官网学习。 PBR是没有了&#xff0c;但是有两个空的Shader 一个是Blank Shader&#xff0c;即空shader&#xff0c;自己进去里面创建PBR(前排提醒&#xff0c;不是简单拖一个PBR nodes&#x…

Substance与PBR工作流总结

关于PBR PBR即基于物理的渲染&#xff0c;是一套尝试基于真实世界光照物理模型的渲染技术合集&#xff0c;使用了一种更符合物理学规律的方式来模拟光线&#xff0c;达到更真实的渲染效果&#xff0c;而且可以直接通过物理参数来直观地达到想要的结果&#xff0c;不用通过拙劣的…

BPR算法

目录 什么是BPR算法 BPR算法简介 显示反馈与隐式反馈 矩阵分解的不足 BPR算法 符号定义 BPR算法解决方式 BPR算法两个基本假设 BPR算法推导 贝叶斯定理 BPR推导 BPR算法流程 BPR算法代码与结果 数据 BPR算法代码 BPR结果展示 什么是BPR算法 BPR算法简介 BPR&…

基于物理的渲染技术(PBR)系列一

笔者介绍&#xff1a;姜雪伟&#xff0c;IT公司技术合伙人&#xff0c;IT高级讲师&#xff0c;CSDN社区专家&#xff0c;特邀编辑&#xff0c;畅销书作者&#xff0c;国家专利发明人;已出版书籍&#xff1a;《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术…

[引擎开发] PBR材质的原理

[本文大纲] 基础概念篇 引入 光线与介质的作用 光线的传播路径 体积散射和表面光照 光线和介质外观 微平面理论 概念介绍 中间向量 能量守恒定律 微平面理论的不足 光照计算 半球积分 …

PBR材质基础概念,限制及未来发展

最近几年图形学社区对PBR的关注非常高&#xff0c;也许是由于Disney以及一些游戏引擎大厂的助推&#xff0c;也许是因为它可以被轻松集成进实时渲染的游戏引擎当中&#xff0c;也许是因为许多人发现现在只需要调几个参数就能实现具有非常精细细节的表面着色了。反正现在网络上随…