解決Python中由于logging模塊誤用導(dǎo)致的內(nèi)存泄露
首先介紹下怎么發(fā)現(xiàn)的吧, 線上的項(xiàng)目日志是通過 logging 模塊打到 syslog 里, 跑了一段時間后發(fā)現(xiàn) syslog 的 UDP 連接超過了 8W, 沒錯是 8 W. 主要是 logging 模塊用的不對
我們之前有這么一個需求, 就是針對每一個連接日志輸出當(dāng)前連接的信息, 所以每一個 連接就創(chuàng)建了一個日志實(shí)例, 并分配一個 Formatter, 創(chuàng)建日志實(shí)例為了區(qū)分其他連接 所以我就簡單粗暴的用了當(dāng)前對象的 id 來作為日志名稱:
import logging class Connection(object): def __init__(self): self._logger_name = "Connection.{}".format(id(self)) self.logger = logging.getLogger(self._logger_name)
當(dāng)然測試環(huán)境是開 DEBUG, 開 DEBUG 就不會往 syslog 里打, 所以不會出現(xiàn) UDP 連接數(shù) 過多, 也就不會知道有內(nèi)存泄露的, 我們來看看這樣為什么會導(dǎo)致內(nèi)存泄露, 首先看看 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
主要調(diào)用了 Logger.manager.getLogger, 這個函數(shù)有下面一段代碼片段
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 模塊為了保證同一個名稱引用同一個日志實(shí)例,所以就把所有的日志實(shí)例全部存 在了一個 loggerDict 的字典里, 所以除非程序退出, 創(chuàng)建的日志實(shí)例引用是不會釋放的, 所以日志實(shí)例里的 handlers 也不會釋放. 之前我又用的對象的 id 來作為日志名稱 的一部分, 所以 SyslogHandler 創(chuàng)建的 UDP 連接就一直被占用導(dǎo)致了過多的 UDP 連接.
為了解決這個問題我在連接關(guān)閉的時候加入了如下代碼:
logging.Logger.manager.loggerDict.pop(self._logger_name) self.logger.manager = None self.logger.handlers = []
按說只加上上面第一行的代碼就應(yīng)該釋放了, 但是沒有, 所以又有了第三行代碼, SyslogHandler 才最終釋放, 這個問題暫時還不知道為什么, 還需要再查查.
2015-03-30 更新 如果日志名稱是以 . 分隔, logging 模塊則會將最后一部分作為日志名, 并往上去尋找 父 Logger, 如果找不到則創(chuàng)建 PlaceHolder 對象作為父, 并引用 Logger.
比如創(chuàng)建的 Logger 名稱為 a.b.c, 那么實(shí)際的名稱則為 c, 并將 b 作為 c 的父, a 作為 b 的 父, 如果沒有該名稱的 Logger 則創(chuàng)建 PlaceHolder 對象作為代替, PlaceHolder 會創(chuàng)建對當(dāng)前 Logger 的引用. 所以需要被回收的日志對象名稱里不應(yīng)包含 .
- python標(biāo)準(zhǔn)日志模塊logging的使用方法
- Python中內(nèi)置的日志模塊logging用法詳解
- Python中使用logging模塊打印log日志詳解
- Python中使用logging模塊代替print(logging簡明指南)
- Python同時向控制臺和文件輸出日志logging的方法
- python改變?nèi)罩?logging)存放位置的示例
- python中使用sys模板和logging模塊獲取行號和函數(shù)名的方法
- python中 logging的使用詳解
- Python使用logging模塊實(shí)現(xiàn)打印log到指定文件的方法
- python 通過logging寫入日志到文件和控制臺的實(shí)例
- 詳解Python中的日志模塊logging
- python logging類庫使用例子
- 詳解Python logging調(diào)用Logger.info方法的處理過程
- 說一說Python logging
- python logging 日志輪轉(zhuǎn)文件不刪除問題的解決方法
- Python中l(wèi)ogging模塊的用法實(shí)例
- Python logging模塊學(xué)習(xí)筆記
- 多個python文件調(diào)用logging模塊報(bào)錯誤
相關(guān)文章
Python自動化辦公實(shí)戰(zhàn)案例詳解(Word、Excel、Pdf、Email郵件)
這篇文章基于Python自動化辦公,主要介紹了使用Python相關(guān)庫,依次完成Word文檔替換、Excel表格讀取、Pdf文件生成和Email自動郵件發(fā)送任務(wù)。感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2021-12-12Python與Matlab實(shí)現(xiàn)快速傅里葉變化的區(qū)別
信號處理免不了要求頻率、畫頻譜圖,但Matlab的fft()函數(shù)與Python的numpy.fft.fft()與scipy.fftpack.fft()函數(shù)得到的是fft變化后的雙邊復(fù)數(shù)值,離畫頻譜圖還有幾句代碼的距離?;驹聿唤榻B了,下面直接懶人投喂,給出Matlab與Python的兩個函數(shù),直接調(diào)用即可畫頻譜圖2021-10-10Python 內(nèi)置函數(shù)之隨機(jī)函數(shù)詳情
這篇文章主要介紹了Python 內(nèi)置函數(shù)之隨機(jī)函數(shù),文章將圍繞Python 內(nèi)置函數(shù)、隨機(jī)函數(shù)的相關(guān)資料展開內(nèi)容,需要的朋友可以參考一下,希望對你有所幫助2021-11-11