前言

提前祝大家过个好年

最近忙于项目,今天抽出点时间写写Blog谈谈昨天遇到的问题

项目最近要收尾了,想把Logger规整一下,因为很多地方都有用到

Python的Logger模块是Python自带的模块,可方便快捷的进行日志的记录

python doc

正文

线程安全

该模块本身就是线程安全的,下面的注释摘抄至 doc

The logging module is intended to be thread-safe without any special work needing to be done by its clients. It achieves this though using threading locks; there is one lock to serialize access to the module’s shared data, and each handler also creates a lock to serialize access to its underlying I/O.

If you are implementing asynchronous signal handlers using the signal module, you may not be able to use logging from within such handlers. This is because lock implementations in the threading module are not always re-entrant, and so cannot be invoked from such signal handlers.

也就是说你不需要关注多线程的问题,只要 getLogger() 时指定当前空间即可

Loggers have the following attributes and methods. Note that Loggers should NEVER be instantiated directly, but always through the module-level function logging.getLogger(name). Multiple calls to getLogger() with the same name will always return a reference to the same Logger object.

The name is potentially a period-separated hierarchical value, like foo.bar.baz (though it could also be just plain foo, for example). Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of foo, loggers with names of foo.bar, foo.bar.baz, and foo.bam are all descendants of foo. The logger name hierarchy is analogous to the Python package hierarchy, and identical to it if you organise your loggers on a per-module basis using the recommended construction logging.getLogger(name). That’s because in a module, name is the module’s name in the Python package namespace.

意思是 logger.getLogger() 时传入相同的变量,会永远返回同一个对象,比如我在当前进程内的任何地方, 使用 log = logger.getLogger("work") 生成的log对象一直是同一个对象,这就是单例模式,官方推荐传入 __name__ 因为他是Python包命名空间中模块的名称。

如果你是纠结 Logger 的单例怎么解决,你可以关闭网页了,因为他本身是单例的

手动写一个单例

手动写一个单例完全是为了记忆单例模式的使用,只是以 logger 模块举例

原始代码

不考虑 logger 的自带单例情况下的原始代码



代码精简过,大致意思不变

测试代码

测试是否可以使用的代码

利用__new__实现单例

我们知道,python实例化时其实是先走 __new__ 再走 __init__

我们可以重写 __new__ 方法,如果发现已生成对象直接返回该对象

同时为了防止多线程的资源竞争,我们使用线程锁来保证同一时间只有一个线程能访问 __new__



但是测试代码跑过之后发现每次会输出接近100条日志,这是为什么呢?

原来,每次请求实例化时,如有对象则直接返回之前生成的对象(MyLogger._instance),但是因为 Python3 默认继承新式类,

Object ,每次请求时返回了 object.__new__ 然后会再执行一遍 MyLogger__init__ 方法,而我们在 __init__ 中添加了两个 Handler ,

而上文提到, logger.getLogger 传入同一个参数则 logger 为一个, 导致每次请求时都会添加两个 Handler 到同一个 logger ,这样导致 loggerHandler 越来越多,重复写入了,解决这个问题需要防止重复走 __init__

其实正确的写法应该是类的 __init__ 只负责接收参数,像这种 add Handler 的功能放到自写方法中,这样 __init__ 不会有任何 add 操作即可

利用元类继承实现单例

该方法利用元类 Type__call__ 实例化的对象调用不会走 __init__ 的特性来规避问题



如果你觉得本方法需要覆盖父类不太好,那么还有第三种方法

自写初始化方法

方法1中,每次都会走 __init__,而我们在 __init__ 中又进行了 add Handler 等操作,那么我们将所有初始化及 add 操作放在自写方法中即可



如上图所示,这对请求实例化的用户是无感知的,它只需要和之前一样调,但其实内部在实例化时调用了 start, 同时重复实例化时走 __init__ 没有任何代码逻辑(走的Object)

小彩蛋

我们在实际测试过程中发现,在配置了log持久化存储搭配多线程使用的时候,写入log文件的日志会丢失数据,测试发现应该是实例化后立刻写入会出现一些延迟,再加上测试代码

写入一条后立刻结束,导致的丢失问题,当然,在实际使用中,一般是初始化时实例化,也不可能在log后直接停止

但是问题还是要解决,实例化我们在每次请求实例化时等待一下即可 time.sleep(1) ,等待时间与机器性能有关,好的机器不会出现问题,

1s是保险的

然后最终代码为

python Logger模块单例模式的更多相关文章

  1. Python logger模块

    1 logging模块简介 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同 ...

  2. python中logger模块的应用

    logger模块是python内置的一个模块,主要用于输出运行日志,可以输出日志的等级,日志的保存路径等 具体详见博客https://www.cnblogs.com/qianyuliang/p/723 ...

  3. python中的logger模块

    logger 提供了应用程序可以直接使用的接口handler将(logger创建的)日志记录发送到合适的目的输出filter提供了细度设备来决定输出哪条日志记录formatter决定日志记录的最终输出 ...

  4. 『无为则无心』Python日志 — 64、Python日志模块logging介绍

    目录 1.日志的作用 2.为什么需要写日志 3.Python中的日志处理 (1)logging模块介绍 (2)logging模块的四大组件 (3)logging日志级别 1.日志的作用 从事与软件相关 ...

  5. Python标准模块--threading

    1 模块简介 threading模块在Python1.5.2中首次引入,是低级thread模块的一个增强版.threading模块让线程使用起来更加容易,允许程序同一时间运行多个操作. 不过请注意,P ...

  6. Python Logging模块的简单使用

    前言 日志是非常重要的,最近有接触到这个,所以系统的看一下Python这个模块的用法.本文即为Logging模块的用法简介,主要参考文章为Python官方文档,链接见参考列表. 另外,Python的H ...

  7. Python标准模块--logging

    1 logging模块简介 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同 ...

  8. python基础-模块

    一.模块介绍                                                                                              ...

  9. python logging模块可能会令人困惑的地方

    python logging模块主要是python提供的通用日志系统,使用的方法其实挺简单的,这块就不多介绍.下面主要会讲到在使用python logging模块的时候,涉及到多个python文件的调 ...

随机推荐

  1. ThreadLocal原理记录,别被坑了!!

    简介 ThreadLocal的用处 ThreadLocal是为了将数据记录一份到某个线程里,确保该数据线程安全 例如数据库的Connection放入ThreadLocal,一个事务会用到很多DAO,但 ...

  2. 短视频去水印v1.0(还支持74个平台)

    软件介绍: 一款很好用的短视频去水印软件,支持74个平台无水印解析,还支持批量视频解析只需要输入账号主页链接就可以了哦,快来下载试试吧!   软件版本:1.0   支持系统:安卓   软件大小:22. ...

  3. 使用OpenSSL自建一个HTTPS服务

    1. 理论知识 1.1 什么是https 传统的 HTTP 协议以明文方式进行通信,不提供任何方式的数据加密,很容易被中间攻击者破解通信内容或者伪装成服务器与客户端通信,在安全性上存在很大问题. HT ...

  4. I/O方式(本章最重要)

    目录 程序查询方式 程序查询方式接口结构 例题 本节回顾 程序中断方式 中断的基本概念 工作流程 中断请求 分类 中断请求标记 中断响应 判优实现 优先级设置 中断处理过程 中断隐指令 硬件向量法 中 ...

  5. ORA-29701: unable to connect to Cluster Synchronization Service

    修改主机名后,has无法启动,将has启动之后,尝试ASMCA,出现如图提示: 再尝试登陆asm实例,出现日下提示: [oracle@edgzrip2-+ASM ~]$ sqlplus / as sy ...

  6. 手把手教你配置KVM服务器

    1 Ubuntu系统安装 1.1 制作启动盘 准备一个U盘,将其清空后,去官网下载Ubuntu18.04系统的iso镜像文件,并将其拷进U盘.然后下载一个UltralOS软碟通工具,完成安装后打开软碟 ...

  7. BloomFilter中保存的数据量

    结果 /** * @author WeiJiQian * BF_CARDINAL_THRESHOLD BF_FALSE_POSITIVE_RATE 保存的数据量 * 100,0000 0.01 391 ...

  8. CTF-源码泄露-PHP备份文件的两种格式

    参考大佬文章: https://www.cnblogs.com/yunqian2017/p/11515443.html https://blog.csdn.net/xy_sunny/article/d ...

  9. Wordpress Polylang 翻译自定义格式

    WordPress 多语言插件 Polylang 主题函数参考 重要:使用一个函数之前,你必须检查函数是否存在,否则,你的网站可能会在 Polylang 更新之前遇到致命错误(因为 WordPress ...

  10. Python将word文档批量转PDF

    前面有一篇<Python批量创建word文档(2)- 加图片和表格>的文章,利用这篇文章创建的word文档来批量转PDF文档.代码: 1 ''' 2 #python批量将word文档转换成 ...