解决Python中由于logging模块误用导致的内存泄露_python

首先介绍下怎么发现的吧, 线上的项目日志是通过 logging 模块打到 syslog 里, 跑了一段时间后发现 syslog 的 UDP 连接超过了 8W, 没错是 8 W. 主要是 logging 模块用的不对

我们之前有这么一个需求, 就是针对每一个连接日志输出当前连接的信息, 所以每一个 连接就创建了一个日志实例, 并分配一个 Formatter, 创建日志实例为了区分其他连接 所以我就简单粗暴的用了当前对象的 id 来作为日志名称:

import logging

class Connection(object):
  def __init__(self):
    self._logger_name = "Connection.{}".format(id(self))
    self.logger = logging.getLogger(self._logger_name)

当然测试环境是开 DEBUG, 开 DEBUG 就不会往 syslog 里打, 所以不会出现 UDP 连接数 过多, 也就不会知道有内存泄露的, 我们来看看这样为什么会导致内存泄露, 首先看看 getLogger 的代码:

def getLogger(name=None):
  """
  Return a logger with the specified name, creating it if necessary.

  If no name is specified, return the root logger.
  """
  if name:
    return Logger.manager.getLogger(name)
  else:
    return root

主要调用了 Logger.manager.getLogger, 这个函数有下面一段代码片段

      if name in self.loggerDict:
        rv = self.loggerDict[name]
        if isinstance(rv, PlaceHolder):
          ph = rv
          rv = (self.loggerClass or _loggerClass)(name)
          rv.manager = self
          self.loggerDict[name] = rv
          self._fixupChildren(ph, rv)
          self._fixupParents(rv)
      else:
        rv = (self.loggerClass or _loggerClass)(name)
        rv.manager = self
        self.loggerDict[name] = rv
        self._fixupParents(rv)

logging 模块为了保证同一个名称引用同一个日志实例,所以就把所有的日志实例全部存 在了一个 loggerDict 的字典里, 所以除非程序退出, 创建的日志实例引用是不会释放的, 所以日志实例里的 handlers 也不会释放. 之前我又用的对象的 id 来作为日志名称 的一部分, 所以 SyslogHandler 创建的 UDP 连接就一直被占用导致了过多的 UDP 连接.

为了解决这个问题我在连接关闭的时候加入了如下代码:

logging.Logger.manager.loggerDict.pop(self._logger_name)
self.logger.manager = None
self.logger.handlers = []

按说只加上上面第一行的代码就应该释放了, 但是没有, 所以又有了第三行代码, SyslogHandler 才最终释放, 这个问题暂时还不知道为什么, 还需要再查查.

2015-03-30 更新 如果日志名称是以 . 分隔, logging 模块则会将最后一部分作为日志名, 并往上去寻找 父 Logger, 如果找不到则创建 PlaceHolder 对象作为父, 并引用 Logger.

比如创建的 Logger 名称为 a.b.c, 那么实际的名称则为 c, 并将 b 作为 c 的父, a 作为 b 的 父, 如果没有该名称的 Logger 则创建 PlaceHolder 对象作为代替, PlaceHolder 会创建对当前 Logger 的引用. 所以需要被回收的日志对象名称里不应包含 .

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索python
python logging、python logging模块、python logging 配置、python logging level、python3 logging模块,以便于您获取更多的相关知识。

时间: 2024-07-31 10:12:02

解决Python中由于logging模块误用导致的内存泄露_python的相关文章

Python中由于logging模块误用导致的内存泄露的解决方法

  Python中由于logging模块误用导致的内存泄露的解决方法         这篇文章主要介绍了解决Python中由于logging模块误用导致的内存泄露,针对由于过多的UDP连接所产生的问题,需要的朋友可以参考下 首先介绍下怎么发现的吧, 线上的项目日志是通过 logging 模块打到 syslog 里, 跑了一段时间后发现 syslog 的 UDP 连接超过了 8W, 没错是 8 W. 主要是 logging 模块用的不对 我们之前有这么一个需求, 就是针对每一个连接日志输出当前连接

Python中使用logging模块打印log日志详解_python

学一门新技术或者新语言,我们都要首先学会如何去适应这们新技术,其中在适应过程中,我们必须得学习如何调试程序并打出相应的log信息来,正所谓"只要log打的好,没有bug解不了",在我们熟知的一些信息技术中,log4xxx系列以及开发Android app时的android.util.Log包等等都是为了开发者更好的得到log信息服务的.在Python这门语言中,我们同样可以根据自己的程序需要打出log. log信息不同于使用打桩法打印一定的标记信息,log可以根据程序需要而分出不同的l

解决Python中字符串和数字拼接报错的方法_python

前言 众所周知Python不像JS或者PHP这种弱类型语言里在字符串连接时会自动转换类型,如果直接将字符串和数字拼接会直接报错. 如以下的代码: # coding=utf8 str = '你的分数是:' num = 82 text = str+num+'分 | 琼台博客' print text 执行结果 直接报错:TypeError: cannot concatenate 'str' and 'int' objects 解决这个方法只有提前把num转换为字符串类型,可以使用bytes函数把int

Python中使用bidict模块双向字典结构的奇技淫巧_python

快速入门 模块提供三个类来处理一对一映射类型的一些操作 'bidict', 'inverted', 'namedbidict' >>> import bidict >>> dir(bidict) ['MutableMapping', '_LEGALNAMEPAT', '_LEGALNAMERE', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'bidict', 'inverted',

Python中使用platform模块获取系统信息的用法教程_python

操作系统相关 system() : 操作系统类型(见例) version(): 操作系统版本 release(): 操作系统发布号, 例如win 7返回7, 还有如NT, 2.2.0之类. platform(aliased=0, terse=0): 操作系统信息字符串,扥与system()+win32_ver()[:3] win32_ver(release='', version='', csd='', ptype=''): win系统相关信息 linux_distribution(distna

在Python中使用M2Crypto模块实现AES加密的教程_python

 AES(英文:Advanced Encryption Standard,中文:高级加密标准),是一种区块加密标准.AES将原始数据分成多个4×4字节矩阵来处理,通过预先定义的密钥对每个字节矩阵中的每个字节进行异或.替换.移位以及线性变换操作来达到加密的目的.密钥长度可以是128,192或256比特.     下面是一个利用Python M2Crypto库,并使用aes_128_ecb算法进行加密和解密的例子.首先介绍一下几个关键的点: 1.iv(Initialization vector),即

Python中使用gzip模块压缩文件的简单教程_python

压缩数据创建gzip文件先看一个略麻烦的做法   import StringIO,gzip content = 'Life is short.I use python' zbuf = StringIO.StringIO() zfile = gzip.GzipFile(mode='wb', compresslevel=9, fileobj=zbuf) zfile.write(content) zfile.close() 但其实有个快捷的封装,不用用到StringIO模块   f = gzip.op

Python中的日志模块logging

  这篇文章主要介绍了Python中的日志模块logging,包括Python下的日志级别以及模块内常用方法的使用,需要的朋友可以参考下 许多应用程序中都会有日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系统的运行状况进行跟踪.在.NET平台中,有非常著名的第三方开源日志组件log4net,c++中,有人们熟悉的log4cpp,而在python中,我们不需要第三方的日志组件,因为它已经为我们提供了简单易用.且功能强大的日志模块:logging.logging模块支持将日志信息保存到不

在Python中使用mechanize模块模拟浏览器功能

  这篇文章主要介绍了在Python中使用mechanize模块模拟浏览器功能,包括使用cookie和设置代理等功能的实现,需要的朋友可以参考下 知道如何快速在命令行或者python脚本中实例化一个浏览器通常是非常有用的. 每次我需要做任何关于web的自动任务时,我都使用这段python代码去模拟一个浏览器. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import mechanize import cookielib # Br