python Logger模块单例模式
前言
提前祝大家过个好年
最近忙于项目,今天抽出点时间写写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 ,这样导致 logger 的 Handler 越来越多,重复写入了,解决这个问题需要防止重复走 __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模块单例模式的更多相关文章
- Python logger模块
1 logging模块简介 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同 ...
- python中logger模块的应用
logger模块是python内置的一个模块,主要用于输出运行日志,可以输出日志的等级,日志的保存路径等 具体详见博客https://www.cnblogs.com/qianyuliang/p/723 ...
- python中的logger模块
logger 提供了应用程序可以直接使用的接口handler将(logger创建的)日志记录发送到合适的目的输出filter提供了细度设备来决定输出哪条日志记录formatter决定日志记录的最终输出 ...
- 『无为则无心』Python日志 — 64、Python日志模块logging介绍
目录 1.日志的作用 2.为什么需要写日志 3.Python中的日志处理 (1)logging模块介绍 (2)logging模块的四大组件 (3)logging日志级别 1.日志的作用 从事与软件相关 ...
- Python标准模块--threading
1 模块简介 threading模块在Python1.5.2中首次引入,是低级thread模块的一个增强版.threading模块让线程使用起来更加容易,允许程序同一时间运行多个操作. 不过请注意,P ...
- Python Logging模块的简单使用
前言 日志是非常重要的,最近有接触到这个,所以系统的看一下Python这个模块的用法.本文即为Logging模块的用法简介,主要参考文章为Python官方文档,链接见参考列表. 另外,Python的H ...
- Python标准模块--logging
1 logging模块简介 logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级.日志保存路径.日志文件回滚等:相比print,具备如下优点: 可以通过设置不同 ...
- python基础-模块
一.模块介绍 ...
- python logging模块可能会令人困惑的地方
python logging模块主要是python提供的通用日志系统,使用的方法其实挺简单的,这块就不多介绍.下面主要会讲到在使用python logging模块的时候,涉及到多个python文件的调 ...
随机推荐
- LeetCode初级算法之数组:136 只出现一次的元素
只出现一次的元素 题目地址:https://leetcode-cn.com/problems/single-number/ 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找 ...
- NOI Online #3 提高组 游记
考的好就来写游记吧 2020.5.24 星期日 上一天晚上为了班里事物做 PPT 肝到 11:30,这比赛就打打玩玩.第二天醒来有点昏昏沉沉的感觉. 打开题面,一看 T1,好像是个性质极其简单的前缀和 ...
- 2020高考倒计时!全屏向下滑动设计HTML源码
全屏竖向滑动效果,自适应,多终端 全国高考倒计时,音乐自动播放. 背景图片:img目录下替换bg.jpg 背景音乐:audio目录下替换song.mp3 原本按照正常情况下每年的6月7.8日就是全国 ...
- Map、Debug追踪
Map集合 概述 现实生活中,我们常会看到这样一种集合:IP地址和主机名,身份证号和个人,系统用户名与系统用户对象等,这种一一对应的关系,我们把它称之为映射.Java当中提供了专门的集合类用来 ...
- eclipse提示JVM版本太低
解决方案:去eclipse的安装路径找到eclipse.ini文件,然后在头部指定JVM的版本(第一第二行) -vm C:/Program Files/Java/jdk-11.0.9/bin -sta ...
- emca配置EM
EM DC(Enterprise Manager Database Control)是 web 界面的数据库管理工具, 可用于配置 EM DC环境的工具包括: Oracle Universal In ...
- Consul 多数据中心下的服务注册发现与配置共享
1. Consul简介 Consul是HashiCorp公司推出的开源软件,它提供了一套分布式高可用可横向扩展的解决方案,能为微服务提供服务治理.健康检查.配置共享等能力. Eurake2.x ...
- wpa_supplicant 检测错误密码
选好了 wifi ssid,填了密码,生成新配置文件,重启了wpa_supplicant,怎么知道输入的密码对不对,如果不对有什么体现? wpa_supplicant 前台运行时,打印信息中会有: W ...
- css 04-CSS选择器:伪类
04-CSS选择器:伪类 #伪类(伪类选择器) 伪类:同一个标签,根据其不同的种状态,有不同的样式.这就叫做"伪类".伪类用冒号来表示. 比如div是属于box类,这一点很明确,就 ...
- python一键搭建ftp服务
from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyf ...