Python中logging实例讲解


Posted in Python onJanuary 17, 2019

logging 的基本用法网上很多,这里就不介绍了。在引入正文之前,先来看一个需求:

假设需要将某功能封装成类库供他人使用,如何处理类库中的日志?

数年前在一个 C# 开发的项目中,我用了这样的方法:定义一个 logging 基类,所有需要用到日志的类都继承这个基类,这个基类中定义一个 LogHandler 事件,该事件用于实现具体的记录日志动作,同时可以通过将类 A 的 LogHandler 委托挂到类 B 的 LogHandler 上,实现将两个类的日志信息添加到一起。

自从看了 python 中 logging 的实现方式,我发现我的做法真是弱爆了。

我在之前的博客 Python:logging.NullHandler 的使用 中介绍了 peewee 框架中的日志输出,简单来说就是 peewee 中定义了一个名为peewee 的 Logger 并添加了一个 NullHandler,调用者只需要为其添加具体的 Handler 就可以输出日志了,非常方便。

Python中logging实例讲解

假设我们在主程序中也有一个 Logger,调用 peewee 后,我想将两个日志输出到同一个日志文件中去。显然将两个日志的 FileHandler 指向同一个日志文件是不可取的,存在并发抢占文件的风险。当然我们也可以将主程序中的 Logger 名字定为 peewee,但这不仅太 low 了,而且如果再调用一个库,其中也封装好了一个 Logger,就不好处理了。

树桩结构的 Logger

Logger 对象被设计为一个树形结构,它有一个 parent 属性。logging 中定义了一个名为 root 的 Logger 作为所有 Logger 的根节点,root 的 parent 属性为 None。root 是全局的。

当调用

logging.getLogger(name=None)

得到一个 Logger 对象的时候,如果 name 为 None,则返回根节点 root。如果 name 中含有 .,比如 name = 'a.b',这时如果已经存在了名为 a 的 Logger,则 a.b 为 a 的子节点,如果不存在名为 a 的 Logger,则 a.b 为 root 的子节点。

child logger 在完成对日志消息的处理后,默认会将日志消息传递给与它的 parent logger。因此,我们不必为一个应用程序中使用的所有 Logger 定义和配置 handlers,只需要为一个顶层的 Logger 配置 handlers,然后按照需要创建 child loggers 就可足够了。我们可以通过设置 Logger 的 propagate 属性设置为 False 来关闭这种传递机制。

什么意思呢,我们来看代码:

import logging
logA = logging.getLogger('a')
logA.setLevel(logging.DEBUG)
logA.addHandler(logging.StreamHandler())
logB = logging.getLogger('a.b')
logB.addHandler(logging.StreamHandler())

输出结果:

Logger A
Logger B
Logger B

之所以 Logger B 被输出了 2 次,是因为 logB 是 logA 的子节点,并且 logB 中也定义了 Handler,所以 logB 的 Handler 输出了一次,logA 的 Handler 也输出了一次,就 2 次了。如果想只输出一次,可以删掉 logB 中的 Handler。当然,这也是有用处的,尤其是当你手头没有日志管理工具的时候。例如,主程序中需要输出所有的日志,以便了解程序整体的运行顺序,而某模块的日志,你想单独输出一份,以便清晰了解模块中的报错或者是执行顺序。

之前 peewee 的例子也就很容易解决了,只需要将 peewee 日志的 parent 属性设置为主程序的日志就可以了。

结语

其实这是一个比较容易说明的问题,完全没必要写这么多。我并不想跟大家分享 python 中的 logging 是怎么用的,而是想和大家分享 logging 如此实现的一种思想,因为我遇到过这个问题,也设计了解决方案,然后被完爆了。

Python 相关文章推荐
对于Python的框架中一些会话程序的管理
Apr 20 Python
Python爬取APP下载链接的实现方法
Sep 30 Python
python爬取NUS-WIDE数据库图片
Oct 05 Python
python 利用文件锁单例执行脚本的方法
Feb 19 Python
Python实现操纵控制windows注册表的方法分析
May 24 Python
pandas的qcut()方法详解
Jul 06 Python
Django之编辑时根据条件跳转回原页面的方法
Aug 21 Python
Python列表删除元素del、pop()和remove()的区别小结
Sep 11 Python
python 如何去除字符串头尾的多余符号
Nov 19 Python
django数据模型中null和blank的区别说明
Sep 02 Python
详解Python yaml模块
Sep 23 Python
详解python百行有效代码实现汉诺塔小游戏(简约版)
Oct 30 Python
python矩阵/字典实现最短路径算法
Jan 17 #Python
python实现Dijkstra静态寻路算法
Jan 17 #Python
解决在Python编辑器pycharm中程序run正常debug错误的问题
Jan 17 #Python
python实现dijkstra最短路由算法
Jan 17 #Python
Pycharm 设置默认头的图文教程
Jan 17 #Python
python实现狄克斯特拉算法
Jan 17 #Python
在PyCharm下使用 ipython 交互式编程的方法
Jan 17 #Python
You might like
桌面中心(二)数据库写入
2006/10/09 PHP
PHP和Mysqlweb应用开发核心技术 第1部分 Php基础-3 代码组织和重用2
2011/07/03 PHP
PHP中去除换行解决办法小结(PHP_EOL)
2011/11/27 PHP
Ajax+PHP快速上手及简单应用说明
2013/07/24 PHP
php判断IP地址是否在多个IP段内
2020/08/18 PHP
使用正则替换变量
2007/05/05 Javascript
JS面向对象编程浅析
2011/08/28 Javascript
JS替换字符串中字符即替换全部而不是第一个
2014/06/04 Javascript
js实现同一个页面多个渐变效果的方法
2015/04/10 Javascript
JS创建对象几种不同方法详解
2016/03/01 Javascript
后端接收不到AngularJs中$http.post发送的数据原因分析及解决办法
2016/07/05 Javascript
详解JS中的attribute属性
2017/04/25 Javascript
Angular实现双向折叠列表组件的示例代码
2017/11/21 Javascript
js构造函数创建对象是否加new问题
2018/01/22 Javascript
Vue.js 2.x之组件的定义和注册图文详解
2018/06/19 Javascript
echarts整合多个类似option的方法实例
2018/07/10 Javascript
layer插件select选中默认值的方法
2018/08/14 Javascript
Python查询Mysql时返回字典结构的代码
2012/06/18 Python
详解Python判定IP地址合法性的三种方法
2018/03/06 Python
python实现简单名片管理系统
2018/11/30 Python
Python lxml解析HTML并用xpath获取元素的方法
2019/01/02 Python
python 实现创建文件夹和创建日志文件的方法
2019/07/07 Python
Python操作远程服务器 paramiko模块详细介绍
2019/08/07 Python
Flask框架搭建虚拟环境的步骤分析
2019/12/21 Python
美国高端婴童品牌:Hanna Andersson
2016/10/30 全球购物
亚历山大·王官网:Alexander Wang
2017/06/23 全球购物
欧洲第一的摇滚和金属乐队服装网站:EMP
2017/10/26 全球购物
微软加拿大官方网站:Microsoft Canada
2019/04/28 全球购物
Java工程师面试集锦之Spring框架
2013/06/16 面试题
自荐信怎么写呢?
2013/12/09 职场文书
个人房屋买卖协议书(范本)
2014/10/04 职场文书
2015年民主生活会发言材料
2014/12/15 职场文书
2015年仓库管理员工作总结
2015/04/21 职场文书
怎样写好工作计划
2019/04/10 职场文书
Mysql数据库事务的脏读幻读及不可重复读详解
2022/05/30 MySQL
win10壁纸在哪个文件夹 win10桌面背景图片文件位置分享
2022/08/05 数码科技