python logging重復(fù)記錄日志問題的解決方法
日志相關(guān)概念
日志是一種可以追蹤某些軟件運(yùn)行時所發(fā)生事件的方法。軟件開發(fā)人員可以向他們的代碼中調(diào)用日志記錄相關(guān)的方法來表明發(fā)生了某些事情。一個事件可以用一個可包含可選變量數(shù)據(jù)的消息來描述。此外,事件也有重要性的概念,這個重要性也可以被稱為嚴(yán)重性級別(level)。
日志的作用
通過log的分析,可以方便用戶了解系統(tǒng)或軟件、應(yīng)用的運(yùn)行情況;如果你的應(yīng)用log足夠豐富,也可以分析以往用戶的操作行為、類型喜好、地域分布或其他更多信息;如果一個應(yīng)用的log同時也分了多個級別,那么可以很輕易地分析得到該應(yīng)用的健康狀況,及時發(fā)現(xiàn)問題并快速定位、解決問題,補(bǔ)救損失。
簡單來講就是,我們通過記錄和分析日志可以了解一個系統(tǒng)或軟件程序運(yùn)行情況是否正常,也可以在應(yīng)用程序出現(xiàn)故障時快速定位問題。比如,做運(yùn)維的同學(xué),在接收到報警或各種問題反饋后,進(jìn)行問題排查時通常都會先去看各種日志,大部分問題都可以在日志中找到答案。再比如,做開發(fā)的同學(xué),可以通過IDE控制臺上輸出的各種日志進(jìn)行程序調(diào)試。對于運(yùn)維老司機(jī)或者有經(jīng)驗(yàn)的開發(fā)人員,可以快速的通過日志定位到問題的根源。可見,日志的重要性不可小覷。日志的作用可以簡單總結(jié)為以下3點(diǎn):
- 程序調(diào)試
- 了解軟件程序運(yùn)行情況,是否正常
- 軟件程序運(yùn)行故障分析與問題定位
如果應(yīng)用的日志信息足夠詳細(xì)和豐富,還可以用來做用戶行為分析,如:分析用戶的操作行為、類型洗好、地域分布以及其它更多的信息,由此可以實(shí)現(xiàn)改進(jìn)業(yè)務(wù)、提高商業(yè)利益。
發(fā)現(xiàn)問題
最近在用Python的logging模塊記錄日志時,遇到了重復(fù)記錄日志的問題,第一條記錄寫一次,第二條記錄寫兩次,第三條記錄寫三次。。。很頭疼,這樣記日志可不行。網(wǎng)上搜索到了原因與解決方案:
原因:沒有移除handler
解決:在日志記錄完之后removeHandler
修改前示例代碼:
import logging
def log(message):
logger = logging.getLogger('testlog')
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.ERROR)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
streamhandler.setFormatter(formatter)
logger.addHandler(streamhandler)
logger.error(message)
if __name__ == '__main__':
log('hi')
log('hi too')
log('hi three')
修改前輸出結(jié)果:
2016-07-08 09:17:29,740 - ERROR - testlog - hi
2016-07-08 09:17:29,740 - ERROR - testlog - hi too
2016-07-08 09:17:29,740 - ERROR - testlog - hi too
2016-07-08 09:17:29,740 - ERROR - testlog - hi three
2016-07-08 09:17:29,740 - ERROR - testlog - hi three
2016-07-08 09:17:29,740 - ERROR - testlog - hi three
修改后示例代碼:
import logging
def log(message):
logger = logging.getLogger('testlog')
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.ERROR)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
streamhandler.setFormatter(formatter)
logger.addHandler(streamhandler)
logger.error(message)
# 添加下面一句,在記錄日志之后移除句柄
logger.removeHandler(streamhandler)
if __name__ == '__main__':
log('hi')
log('hi too')
log('hi three')
修改后輸出結(jié)果:
2016-07-08 09:32:28,206 - ERROR - testlog - hi
2016-07-08 09:32:28,206 - ERROR - testlog - hi too
2016-07-08 09:32:28,206 - ERROR - testlog - hi three
深度解析:
Google之后,大概搞明白了,就是你第二次調(diào)用log的時候,根據(jù)getLogger(name)里的name獲取同一個logger,而這個logger里已經(jīng)有了第一次你添加的handler,第二次調(diào)用又添加了一個handler,所以,這個logger里有了兩個同樣的handler,以此類推,調(diào)用幾次就會有幾個handler。。
所以這里有以下幾個解決辦法:
- 每次創(chuàng)建不同name的logger,每次都是新logger,不會有添加多個handler的問題。(ps:這個辦法太笨,不過我之前就是這么干的。。)
- 像上面一樣每次記錄完日志之后,調(diào)用removeHandler()把這個logger里的handler移除掉。
- 在log方法里做判斷,如果這個logger已有handler,則不再添加handler。
- 與方法2一樣,不過把用pop把logger的handler列表中的handler移除。
下面是方法3與方法4的代碼示例:
方法3:
import logging
def log(message):
logger = logging.getLogger('testlog')
# 這里進(jìn)行判斷,如果logger.handlers列表為空,則添加,否則,直接去寫日志
if not logger.handlers:
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.ERROR)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
streamhandler.setFormatter(formatter)
logger.addHandler(streamhandler)
logger.error(message)
if __name__ == '__main__':
log('hi')
log('hi too')
log('hi three')
方法4:
import logging
def log(message):
logger = logging.getLogger('testlog')
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.ERROR)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
streamhandler.setFormatter(formatter)
logger.addHandler(streamhandler)
logger.error(message)
# 用pop方法把logger.handlers列表中的handler移除,注意如果你add了多個handler,這里需多次pop,或者可以直接為handlers列表賦空值
logger.handlers.pop()
# logger.handler = []
if __name__ == '__main__':
log('hi')
log('hi too')
log('hi three')
這幾種方法都親試可行,個人覺得方法3判斷更加優(yōu)雅,你覺得呢?
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
python數(shù)據(jù)可視化自制職位分析生成崗位分析數(shù)據(jù)報表
之前網(wǎng)上也有不少關(guān)于行業(yè)的分析數(shù)據(jù),今天我們就根據(jù)不同崗位,公司類型規(guī)模,學(xué)歷要求,薪資分布等來進(jìn)行分析,把職位分析功能集合封裝起來,做成一個小工具分享給大家吧2021-09-09
Python+PyQt5實(shí)現(xiàn)自動點(diǎn)擊神器
這篇文章主要為大家詳細(xì)介紹了如何利用Python和PyQt5實(shí)現(xiàn)自動點(diǎn)擊神器,旨在解決重復(fù)性的點(diǎn)擊工作,解放雙手,具有及時性和準(zhǔn)確性,需要的可以參考下2024-01-01
深入分析python數(shù)據(jù)挖掘 Json結(jié)構(gòu)分析
這篇文章通過實(shí)例給大家分析總結(jié)了python數(shù)據(jù)挖掘以及Json結(jié)構(gòu)分析的相關(guān)知識點(diǎn),對此有興趣的朋友參考下。2018-04-04
pyinstaller打包單個exe后無法執(zhí)行錯誤的解決方法
今天小編就為大家分享一篇pyinstaller打包單個exe后無法執(zhí)行錯誤的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-06-06
詳解pandas映射與數(shù)據(jù)轉(zhuǎn)換
這篇文章主要介紹了pandas映射與數(shù)據(jù)轉(zhuǎn)換的相關(guān)資料,幫助大家更好的利用python進(jìn)行數(shù)據(jù)分析,感興趣的朋友可以了解下2021-01-01
Python生成可執(zhí)行文件之PyInstaller庫的使用方式
PyInstaller是一個十分有用的第三方庫,通過對源文件打包,Python程序可以在沒有安裝Python的環(huán)境中運(yùn)行,也可以作為一個獨(dú)立文件方便傳遞和管理,下面這篇文章主要給大家介紹了關(guān)于Python生成可執(zhí)行文件之PyInstaller庫的使用方式,需要的朋友可以參考下2022-04-04
使用Python的urllib和urllib2模塊制作爬蟲的實(shí)例教程
這篇文章主要介紹了使用Python的urllib和urllib2模塊制作爬蟲的實(shí)例教程,展現(xiàn)了這兩個常用爬蟲制作模塊的基本用法,極度推薦!需要的朋友可以參考下2016-01-01

