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文件的调 ...
随机推荐
- react路由初探(2)
对着官网的例子反正是没有搞出来,所以搜了一大堆,最终搞出来了,记录一下 import React from 'react'; // 首先我们需要导入一些组件... (这个是中文网示例,按这个做,报一大 ...
- 换种思路写Mock,让单元测试更简单
开篇引入 单元测试中的Mock方法,通常是为了绕开那些依赖外部资源或无关功能的方法调用,使得测试重点能够集中在需要验证和保障的代码逻辑上.在定义Mock方法时,开发者真正关心的只有一件事:" ...
- 【题解】「UVA681」Convex Hull Finding
更改了一下程序的错误. Translation 找出凸包,然后逆时针输出每个点,测试数据中没有相邻的边是共线的.多测. Solution 首先推销一下作者的笔记 由此进入>>> ( ...
- TCP 百万并发 数据连接测试 python+locust
过程笔记和总结 尝试一.locust 测试百万Tcp并发 另一种方式是使用jmeter 基础环境 服务端 虚拟机:Centos7.2 jdk 1.8 客户端 虚拟机: Centos7.2 python ...
- 第一篇:docker 简单入门(一)
本篇目录 写在最前面的话 docker概念介绍 镜像的概念.容器的概念 docker的安装介绍 写在最前面的话 由于此类文章博客园鉴定为简单文章,所以已经移到csdn[https://blog.csd ...
- C语言服务器编程必备常识
入门 包含了正确的头文件只能编译通过,没链接正确的库链接会报错. 一些常用的库gcc会自动链接. 库的缺省路径/lib /usr/lib /usr/local/lib 不知道某个函数在那个库可以nm ...
- vue第九单元(非父子通信 events 单向数据流)
第九单元(非父子通信 events 单向数据流) #课程目标 了解非父子组件通信的原理,熟练实现非父子组件间的通信(重点) 了解单向数据流的含义,并且明白单向数据流的好处 #知识点 #1.非父子组件间 ...
- [日常摸鱼]bzoj1038[ZJOI2008]瞭望塔-半平面交
这回好好用半平面交写一次- 看了cls当年写的代码看了好久大概看懂了-cls太强辣 #include<cstdio> #include<iostream> #include&l ...
- matplotlib的学习13-subplot分格显示
import matplotlib.pyplot as plt plt.figure()#创建一个图像窗口 # 使用plt.subplot2grid来创建第1个小图, (3,3)表示将整个图像窗口分成 ...
- dropload.min.js 下拉刷新后,无法上拉加载更多
使用方法 1.引入文件 <script src="/app/media/js/dropload.min.js"></script> 111111111111 ...