前言

提前祝大家过个好年

最近忙于项目,今天抽出点时间写写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. 团队展示——Part I

    1. 团队简介 队名:非专业团队

  2. 两种方式简单免杀ew

    1.资源操作法 使用工具: Restorator 2018 BeCyIconGrabber 首先我们从github下载ew使用360进行查杀 打开Restorator 将ew拖入,右键添加资源 选择图 ...

  3. 基于jaccard相似度的LSH

    使用Python通过LSH建立推荐引擎 LSH:一个可以用来处理成百上千行的算法 前提: Python 基础 Pandas 学完本教程之后,解锁成就: 通过建立shingles 为LSH准备训练集和测 ...

  4. NOI Online #2 提高组 游记

    没 NOI Online 1 挂的惨就来写游记吧,不知道为啥 NOI Online 1 民间数据测得 60 分的 T1 最后爆零了... 昏昏沉沉的醒来,吃了早饭,等到 \(8:30\) 进入比赛网页 ...

  5. CF850F Rainbow Balls 题解

    考虑最后变成哪一种颜色. 设 \(s = \sum\limits_{i=1}^n a_i\) 设现在有 \(k\) 种当前颜色, 需要全部变成该种颜色, 期望步数为 \(f_k\). 考虑状态转移.设 ...

  6. Angular:自定义属性指令

    ①在命令行窗口下用 CLI 命令ng g directive创建指令类文件 ②将directives/light.directive.ts文件改造一番 import { Directive, Elem ...

  7. 图的遍历BFS

    图的遍历BFS 广度优先遍历 深度优先遍历 可以进行标记 树的广度优先遍历,我们用了辅助的队列 bool visited[MAX_VERTEX_NUM] //访问标记数组 //广度优先遍历 void ...

  8. 女朋友突然问我DNS是个啥....

    女朋友突然问我DNS是个啥.... 今天晚上我正在床上躺着刷手机,然后我女朋友突然说她的电脑坏了.说连着WIFi上不了网,让我给她看一下.(这就是有个程序员男朋友的好处) 然后我拿到电脑看了一下发现访 ...

  9. 主从复制直接转换MGR_5.7验证试验

    环境信息 IP port role info 192.168.188.51 4000 node1 master 192.168.188.52 4000 node2 slave1 192.168.188 ...

  10. 在win7下安装texlive

    首先到texlive官网下载texlive2017.iso 然后解压,找到install-tl-advanced.bat,用管理员权限双击,安装,关闭杀毒软件. 基本选择默认,前面路径可以按自己需求更 ...