Python logging 模块

2023-10-31

Python logging 模块

给大家剖析下Python的logging模块。

logging模块是Python内置的一个强大易用的日志模块。简单到你只需要两行代码就能输出一些东西来:

import logging
logging.warning('Hi, I print something.')

输出:

WARNING:root:Hi, I print something.

当然,我们可不能满足于此,你也很容易发现把上面 warning 改成 info 就输不出来了。别急,往下看。

1. 先大体概括下logging的结构

  • 整体上来说,日志管理最大的结构是Manager,Manager管理所有的Logger,但你不用去管这个东西,它是藏在背后默默干活的。
  • Logger里最根部是一个RootLogger,name是root,其他所有Logger都是在这个Logger下,通过名字,形成父子、子孙的树杈结构。
  • Logger里面有一些东西,Filter、Handler、LogRecord、Formatter,可以看作是一些配置,各自有各自的作用,接下来会介绍。
  • 还能用LoggerAdapter对Logger进行一定的包装,一般我们用的很少。

logger是一个树结构,默认有个根root,其他logger都是其上的枝桠,比如创建一个name=’A.B’的logger,其实际结构就是 root-A-B

root-A-B结构

再创建一个name=’A.C.D’的logger,结构变为:

root-A-B-C-D结构

2. level —— 输出哪个级别的日志

在输出log的时候,不能所有日志都输出出来,而是有所选择,比如有时候我们只希望看到warning以上严重程度的,如果有太多info、debug会让log可读性变得很差。

我们可以通过设置logger的level来实现这一点。在logging中,将level设置如下:

LevelName LevelValue
CRITICAL/ FATAL 50
ERROR 40
WARNING/ WARN 30
INFO 20
DEBUG 10
NOTSET 0

示例:

import logging
logger = logging.getLogger('test')
logging.basicConfig()  # basicConfig是logging提供的简单的配置方法,不用basicConfig则需要手动添加handler
logger.setLevel(logging.INFO)  # 输出所有大于等于INFO级别的log
logger.info('I am <info> message.')
logger.debug('I am <debug> message.')  # 不输出

结果正如所料,info输出,debug未输出。

INFO:test:I am <info> message.

要注意的是: root 的默认级别是 WARNING!, 而且logger实际输出时的level是取决于EffectiveLevel,即从该级往上走,遇到的第一个level不为0的logger的level,也就是说如果你创建了logger,而没有为其设置level,那它默认是NOTSET,程序会往上层找,直到root,而root级别是WARNING,所以可能会导致没有输出日志。

3. handler —— 输出到哪里

我们写日志一个很重要的问题就是把日志输出到什么地方去,我们可能希望某些日志在console打印出来,可能希望有更详细的日志输出到log文件里去。怎么控制这些输出就需要用handler了。

import logging
logger = logging.getLogger('test')
logger.addHandler(logging.StreamHandler())  # 添加StreamHandler
logger.setLevel(logging.INFO)  # 输出所有大于INFO级别的log
logger.info('I am <info> message.')
logger.debug('I am <debug> message.')  # 不输出

我们把上面的例子稍微改动了一下,可以看到输出如下,输出到了console里

I am <info> message.

这就是logging提供的最基本的一个handler,其他各种handler都是从这个handler继承发展来的。理论上可以把日志输出到各种流中,stderr、文件、socket等都可以。当然logging已经将各种流handler封装好了。

如:

import logging
logger = logging.getLogger('test')
logger.addHandler(logging.StreamHandler())
logger.addHandler(logging.FileHandler('test.log'))  # 再添一个FileHandler
logger.setLevel(logging.INFO)  # 输出所有大于INFO级别的log
logger.info('I am <info> message.')
logger.debug('I am <debug> message.')  # 不输出

可以看到,info不仅仅输出到了console中,还在当前文件夹下创建了一个test.log文件并输出到了该文件中。在logging.handlers中还封装了一堆更高级的handlers,可以了解下,尤其是RotatingFileHandler和TimedRotatingFileHandler,可以把你的日志按一定规则分割成多份。你也可以自己封装handler哦,网上有人这么干的。

上面我们看到了logger的级别,可以控制这个logger要输出什么级别的log。但这里我们发现可以在logger里添加handler,控制输出log到哪里,明显发现,其实我们想要在不同的handler里输出不同级别的日志。

比如我们想要在console里输出warning以上的日志,在log文件里输出debug以上的日志,该怎么办呢?

handler也是有级别的。

如下:

import logging
logger = logging.getLogger('test')
logger.setLevel(logging.INFO)  # 输出所有大于INFO级别的log

# 添加StreamHandler,并设置级别为WARNING
stream_hdl = logging.StreamHandler()
stream_hdl.setLevel(logging.WARNING)
logger.addHandler(stream_hdl)
# 添加FileHandler,并设置级别为DEBUG
file_hdl = logging.FileHandler('test.log')
file_hdl.setLevel(logging.DEBUG)
logger.addHandler(file_hdl)

logger.info('I am <info> message.')
logger.debug('I am <debug> message.')  # 不输出

设置了WARNING级别的StreamHandler以及DEBUG级别的FileHandler。在logger.info()执行时,程序判断log级别(info) >= logger级别(INFO),可以输出。

然后程序判断log级别(info) < StreamHandler级别(WARNING),不在stderr输出;同时log级别(info) > FileHandler级别(DEBUG),在test.log中输出。

而logger.debug()的级别 < logger级别(INFO),所以不会在stderr和test.log输出。

其实上面的程序有个问题,就是设置了logger是INFO,那么FileHandler是DEBUG没有什么意义的,DEBUG级别的日志过不了logger那关。

你在写代码的时候就需要注意了,要给logger添加handler,并且注意logger和handler的level,否则可能会输不出你想要的log哦。

4. formatter —— 输出日志的格式

细心的话可以发现,我们后来自己添加的handler输出的log是没有格式的,就仅仅是输出而已。但basicConfig()输出的log是有格式的(虽然很丑)。

不同在于basicConfig()中的handler是带有formatter的。我们要添加formatter就需要用到logging中的另一个类Formatter

import logging
logger = logging.getLogger('test')
logger.setLevel(logging.INFO)  # 输出所有大于INFO级别的log
fmt = logging.Formatter('%(name)s - %(levelname)s - %(asctime)s - %(message)s')
# 添加StreamHandler,并设置级别为WARNING
stream_hdl = logging.StreamHandler()
stream_hdl.setLevel(logging.WARNING)
stream_hdl.setFormatter(fmt)
logger.addHandler(stream_hdl)
# 添加FileHandler,并设置级别为DEBUG
file_hdl = logging.FileHandler('test.log')
file_hdl.setLevel(logging.DEBUG)
file_hdl.setFormatter(fmt)
logger.addHandler(file_hdl)

logger.info('I am <info> message.')
logger.debug('I am <debug> message.')  # 不输出

如上例中,我们添加了formatter,格式为 (logger name) - (level name) - (当前时间) - (日志信息),输出的结果为:

test - INFO - 2017-09-06 16:45:36,977 - I am <info> message.

是不是漂亮多了,logging的formatter可以输出的不止这几个信息,还有很多:

  • %(name)s ——Name of the logger (logging channel)
  • %(levelno)s ——Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  • %(levelname)s ——Text logging level for the message (“DEBUG”, “INFO”, “WARNING”, “ERROR”, “CRITICAL”)
  • %(pathname)s ——Full pathname of the source file where the logging call was issued (if available)
  • %(filename)s ——Filename portion of pathname
  • %(module)s ——Module (name portion of filename)
  • %(lineno)d ——Source line number where the logging call was issued(if available)
  • %(funcName)s ——Function name
  • %(created)f ——Time when the LogRecord was created (time.time() return value)
  • %(asctime)s ——Textual time when the LogRecord was created
  • %(msecs)d ——Millisecond portion of the creation time
  • %(relativeCreated)d ——Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded (typically at application startup time)
  • %(thread)d ——Thread ID (if available)
  • %(threadName)s ——Thread name (if available)
  • %(process)d ——Process ID (if available)
  • %(message)s ——The result of record.getMessage(), computed just as the record is emitted

我们也可以给Formatter传入第二个参数,修改日期格式,比如 %Y-%m-%d ,则输出的log就没有时分秒了。

这样就可以定制log的格式了,还可以为不同的handler制定不同的formatter,以适应不同情况下的阅读或传输需求。

5. Filter和LoggerAdapter

最后这里有两个我们不常用到的东西,懒得多写了。

6. 重复写日志的问题

这里有一个坑需要特别说明下,就是add handler的问题。一个logger创建之后不要重复添加handler,有时候我们多个文件都调用我们封装好的logger的类,就有可能会发生重复添加handler的情况,具体的看博主这篇文章——python logging 重复写日志问题

7. 差点忘了

差点忘了配置的问题,我们可以通过多种途径配置logger,像上面用过的basicConfig()(没有用参数,可以自己阅读源码,很简单的几个参数),或者像上面的例子手动添加handler、设置formatter,logging还给了我们其他高级的配置方法:

logging.config里有多种配置方法,像fileConfig,可以直接读一个文件;像dictConfig,可以传一个dict进行配置(django就是这么配置的);似乎还能起个socket,来实时修改config,听起来很吊的样子。

不过我们用其中的一种就可以了,随便配喽。

比如,像博主这样 怎样从0开始搭建一个测试框架_2

就说这些,跟其他人的讲法好像不太一样,结合着来看吧。

有什么好的建议或者问题,可以留言或者加我的QQ群:455478219讨论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python logging 模块 的相关文章

  • 如何在python包中包含.pyx文件

    我在我的包中使用了 cythonpyirt https github com 17zuoye pyirt 但是当我将其发布到 pypi 时 pyx 文件不包含在 tar gz 中 我认为这一定与安装文件有关 但是 我找不到解决这个问题的方法
  • PySerial 和多个 Python 安装出现问题

    我的 Windows 7 计算机上有 Python 2 4 4 和 3 1 3 我想使用 PySerial 听说是内置的 所以我尝试了一下import serial在两个版本中 两者都造成了Import Error 然后我从以下位置下载了w
  • 在python中浏览ftp目录

    我正在尝试使用 ftplib 使用 Python 3 从 ftp 服务器下载多个文件夹 我有一个文件夹名称列表 它们都位于文件夹 root 中 问题是我不知道如何浏览它们 当我使用cwd我可以进入更深的目录 但是如何再次起来呢 我正在尝试得
  • 如何在 pySpark 数据框中添加行 ID [重复]

    这个问题在这里已经有答案了 我有一个 csv 文件 我在 pyspark 中将其转换为 DataFrame df 经过一番改造后 我想在 df 中添加一列 这应该是简单的行 ID 从 0 或 1 开始到 N 我将 df 转换为 rdd 并使
  • Logback 配置在单行上有异常吗?

    我的日志被提取 传输并合并到 elasticsearch 中 多行事件很难跟踪和诊断 有没有办法使用收集器和正则表达式将异常行分组到单个记录中登录配置 https logback qos ch manual layouts html xTh
  • Pandas Dataframe.to_csv 小数=',' 不起作用

    在 Python 中 我正在将 Pandas Dataframe 写入 csv 文件 并希望将小数分隔符更改为逗号 像这样 results to csv D Data Kaeashi BigData ProcessMining Voorbe
  • 如何计算具有较大中间值的总和

    我想计算 for n m两个值都是 1000 以内的整数 最终结果是一个不大于 1000 的数字n但中间值对于 python 来说太大了 无法处理 你怎么解决这个问题 我将函数定义如下 from scipy misc import comb
  • 如何在 C++ 中的嵌套词法作用域可访问的作用域中声明静态信息?

    我想声明范围的标识符 该标识符将用于自动填充最内部范围内任何日志记录语句的字段 它们通常会 但并非总是如此 例如 lambda 使用 引入的块 匹配封闭块的 名称 用法看起来像这样 namespace app LOG CONTEXT app
  • python请求:重试直到收到有效响应

    我想知道是否存在重试请求一定次数的常见模式 可能由于服务器错误或网络不良而失败 我想出了这个 并且我愿意在那里找到更好的实现 cnt 0 while cnt lt 3 try response requests get uri if res
  • seaborn 未在定义的子图中绘制

    我正在尝试用这段代码并排绘制两个图表 fig ax1 ax2 plt subplots 1 2 sns displot x X train Age hue y train ax ax1 sns displot x X train Fare
  • Altair 条形图具有可变宽度的条形?

    我正在尝试在 Python 中使用 Altair 制作条形图 其中条形的宽度根据源数据帧列中的数据而变化 最终目标是获得如下所示的图表 条形的高度对应于每种能源技术的边际成本 在源数据框中以列形式给出 条形宽度对应于每种能源技术的容量 也以
  • 如何在Python模拟中调用模拟方法

    我想创建一个模拟方法来调用被模拟的底层方法 我正在想象类似以下的内容 但我找不到任何有关模拟对象的文档 该对象包含对被模拟对象的引用 我将其表示为 wrapped method foo below from mock import patc
  • 如何使用 Tkinter 创建等宽网格列?

    如何强制 Tkinter 应用程序窗口中的列宽度相等 tkdocs网站声明如下 每列的宽度 或每行的高度 取决于列或行中包含的小部件的宽度或高度 这意味着当绘制用户界面并将其划分为行和列时 您无需担心每列或行的宽度相等 或高度 大概 TkD
  • Django外键:获取相关模型?

    是否可以通过外键字段本身获取外键的相关模型 例如 如果我有 3 个模型 class ModelA models Model field1 models CharField max length 10 class ModelB models
  • 您能否从函数、args 和 kwargs 确定变量将如何分配?

    我有一些样板逻辑 我想包装几个具有相同可选关键字的函数 现在看起来像下面的代码 但是 这仅处理 opt key 作为关键字传递的情况 而不是按位置传递 解决这个问题的一种方法是了解如何解决参数分配 是否有一些元函数接受函数 args 和 k
  • 使用 pyinstaller 制作的可执行文件出现运行时错误

    所以我使用 Pygame 制作了一个游戏 现在我想用它制作一个可执行文件 首选独立可执行文件 所以我用它来制作可执行文件 pyinstaller onefile main py 编译顺利 但运行时出现错误 这是错误 Traceback mo
  • Python:如何访问 Lotus Notes 8.5 Inbox 来阅读电子邮件

    我想用 python 创建一个脚本 从 Lotus Notes 8 5 读取电子邮件 然后在 jira 中为每封电子邮件创建一个问题 但当我尝试从 Lotus 读取邮件时 它会返回此错误 Traceback most recent call
  • 如何使用 Python 3 在 OpenCV 3 上正确加载 cv2.KeyPoint 和描述符?

    有一天 我不得不恢复一个使用 OpenCV 3 和 Python 2 7 的旧项目 在此代码中 要加载 cv2 KeyPoint 我执行以下操作 import numpy as np import cPickle import cv2 ke
  • 从另一个列表的元素创建一个新列表,引用后者的元素

    我想从前一个元素创建一个新列表 但不复制它们 这就是发生的事情 In 23 list range 10 In 24 list2 list 0 4 In 25 list Out 25 0 1 2 3 4 5 6 7 8 9 In 26 lis
  • 带有 graphviz_layout 的水平树

    在Python中 使用networkx 我可以用以下方法绘制垂直树 g nx balanced tree 2 4 pos nx graphviz layout g prog dot nx draw g pos labels b all no

随机推荐

  • 【日志首次上报积分最多】

    题目描述 日志首次上报最多积分 日志采集是运维系统的的核心组件 日志是按行生成 每行记做一条 由采集系统分批上报 如果上报太频繁 会对服务端造成压力 如果上报太晚 会降低用户的体验 如果一次上报的条数太多 会导致超时失败 为此 项目组设计了
  • windows能访问外网但无法访问虚拟机

    最近一直遇到个头疼的问题 Windows本机上搭了几个Linux虚拟机 不知怎么的 使用Linux虚拟机两两互相访问是可以互相ping通的 但是用Windows主机去访问内部的虚拟机老是提示 无法访问目标主机 第一步 确定虚拟机和Windo
  • Android之RecyclerView线性列表、网格列表实现滑动到指定位置并置顶

    文章目录 前言 一 使用步骤 1 LinearLayoutManager 2 GridLayoutManager 3 LinearSmoothScroller 4 使用 总结 前言 在日常开发中 我们经常会遇到选择事物的需求 例如单选多选
  • Alibaba开源框架COLA的个人心得--COLA框架各层职责

    COLA框架各层职责 1 官网定义 1 适配层 Adapter Layer 负责对前端展示 web wireless wap 的路由和适配 对于传统B S系统而言 adapter就相当于MVC中的controller 2 应用层 Appli
  • 快速排序,快速选择排序,选择排序的区别

    选择排序是对整体序列进行排序 快速排序也是对整体序列进行排序 快速选择算法是快速在未排序的数组中寻找第k小 大的元素 快速选择算法和快速排序的思想是找基准点 在基准点左边的都比他小 然后在基准点右边的都比大这样 但是这两个算法的目的不同 所
  • 硬币系列二

    最近搞了一些稀奇硬币 老潘把他们都用手机拍了下来 但是由于手机镜头焦距所限 并不能让硬币充满整个画面 所以很自然的想法就是 把硬币从图片中裁剪出来 一个正常人的做法是 把需要拍摄特写的物品放在纯净颜色的背景上 这种做法其实也有利于后期的抠图
  • STM32--CAN ID过滤器分析

    1 前言 在CAN协议里 报文的标识符不代表节点的地址 而是跟报文的内容相关的 因此 发送者以广播的形式把报文发送给所有的接收者 节点在接收报文时 根据标识符 CAN ID 的值决定软件是否需要该报文 如果需要 就拷贝到SRAM里 如果不需
  • 配合插件flatten-maven-plugin及${revision}属性在maven多模块项目中可进行全局版本号管理

    父pom
  • 【原创】基于FPGA的数码管按键显示(two_key)

    文档说明 一 背景介绍 心理一直有个疙瘩 总感觉不把数码管玩的溜些 觉得少了些什么东西 念头不通畅 说什么也要拿下 于是 有了这篇文档的产生 二 项目需求 选用开发板上的2个按键 当s1按下后 key cnt计数加2 当s2按下后 key
  • 函数printf()的格式转换

    知识点自用 c语言程序设计p43
  • C# 简介(详细)

    C 是一个现代的 通用的 面向对象的编程语言 它是由微软 Microsoft 开发的 由 Ecma 和 ISO 核准认可的 C 是由 Anders Hejlsberg 和他的团队在 Net 框架开发期间开发的 C 是专为公共语言基础结构 C
  • Android Framework学习(七)之Thread类以及常用同步类

    前言 Thread类是Android为线程操作而做的一个封装 代码在Thread cpp中 其中还封装了一些与线程同步相关的类 本篇博客 我们一起学习Thread类以及常用同步类 Thread类 Thread类的构造函数中的有一个canCa
  • 漫画:什么是服务熔断?

    点击上方 程序员小灰 选择 置顶公众号 有趣有内涵的文章第一时间送达 小灰的答题策略 是遇到难题不断思考 不断思考 一直到考试结束为止 可以用下面这张图流程图来表示 大黄的答题策略 是遇到难题先尝试几次思考 如果尝试一定次数不成功 则果断放
  • vcruntime140_1.dll文件下载及安装方法,详细修复方案

    最近在玩游戏跟打开ps的时候 电脑莫名出现上出现了一个名为vcruntime140 1 dll的错误提示 这个错误提示让我无法正常运行一些软件和游戏 给我的电脑使用带来了很大的困扰 第一时间我就在网上翻阅各种关于vcruntime140 1
  • Python机器视觉--OpenCV进阶(核心)--滤波器之卷积介绍

    滤波器之卷积介绍 1 卷积 1 1 什么是图片卷积 图像卷积就是卷积核在图像上按行滑动遍历像素时不断的相乘求和的过程 1 2 卷积步长 步长就是卷积核在图像上移动的步幅 上面例子中卷积核每次移动一个像素步长的结果 如果将这个步长修改为2 结
  • 根据指定时间获取时间(日,周,月,季度,年)

    package Test import java text ParseException import java text SimpleDateFormat import java util TODO author biao date 20
  • linux下安装使用libuuid

    UUID简介 UUID含义是通用唯一识别码 Universally Unique Identifier 这 是一个软件建构的标准 也是被开源软件基金会 Open Software Foundation OSF 的组织应用在分布式计算环境 D
  • 基于python的socket通信

    准备阶段 需要导入socket库和threading库 如没有请自行安装 本次任务是在python3 9的编译器下运行 版本可自己选择 并不需要相同 关于socket socket 的典型应用就是 Web 服务器和浏览器 浏览器获取用户输入
  • 01 shell脚本中常用命令

    shell 常用命令 数据检索命令 过滤 grep egrep 字符串检索 cut tr 数据处理命令 数据排序 sort 数据去重 uniq 文本数据合并 paste 数据输出 tee 数据处理 xargs 1 grep 负责从数据源中检
  • Python logging 模块

    Python logging 模块 给大家剖析下Python的logging模块 logging模块是Python内置的一个强大易用的日志模块 简单到你只需要两行代码就能输出一些东西来 import logging logging warn