㈠ 多進程環境python logging列印日誌混亂問題
解決辦法如下:
多麼痛的領悟,滑腔困擾了這么久的問題其實就是一個參數配置錯了。
fileMode:表示日誌山讓侍文件的打開方式。w-直接寫,使用這個配置當系統重啟的時候日誌會清空,一個進程打開後其他進程是無法使用的;a-尾部逗吵追加,大家都可以打開往文件結尾進行追加寫入。
本人主語言是java,轉到python後日誌這塊踩了幾個坑。再說說另外一個坑,就是異常堆棧的列印問題,在java中logger是可以使用error直接列印出來的。在python中error跟其他日誌記錄方法沒太大差別,是無法列印異常堆棧的,列印堆棧請使用 logger.exception("異常說明", e) 。
㈡ python 多進程
基於官方文檔:
https://docs.python.org/zh-cn/3/library/multiprocessing.html
日樂購,剛才看到的一個博客,寫的都不太對,還是基於官方的比較穩妥
我就是喜歡抄官方的,哈哈
通常我們使用Process實例化一個進程,並調用 他的 start() 方法啟動它。
這種方法和 Thread 是一樣的。
上圖中,我寫了 p.join() 所以主進程是 等待 子進程執行完後,才執行 print("運行結束")
否則就是反過來了(這個不一定,看你的語句了,順序其實是隨機的)例如:
主進加個 sleep
所以不加join() ,其實子進程和主進程是各干各的,誰也不等誰。都執行完後,文件運行就結束了
上面我們用了 os.getpid() 和 os.getppid() 獲取 當前進程,和父進程的id
下面就講一下,這兩個函數的用法:
os.getpid()
返回當前進程的id
os.getppid()
返回父進程的id。 父進程退出後,unix 返回初始化進程(1)中的一個
windows返回相同的id (可能被其他進程使用了)
這也就解釋了,為啥我上面 的程序運行多次, 第一次列印的parentid 都是 14212 了。
而子進程的父級 process id 是調用他的那個進程的 id : 1940
視頻筆記:
多進程:使用大致方法:
參考: 進程通信(pipe和queue)
pool.map (函數可以有return 也可以共享內存或queue) 結果直接是個列表
poll.apply_async() (同map,只不過是一個進程,返回結果用 xx.get() 獲得)
報錯:
參考 : https://blog.csdn.net/xiemanR/article/details/71700531
把 pool = Pool() 放到 if name == " main ": 下面初始化搞定。
結果:
這個肯定有解釋的
測試多進程計算效果:
進程池運行:
結果:
普通計算:
我們同樣傳入 1 2 10 三個參數測試:
其實對比下來開始快了一半的;
我們把循環里的數字去掉一個 0;
單進程:
多進程:
兩次測試 單進程/進程池 分別為 0.669 和 0.772 幾乎成正比的。
問題 二:
視圖:
post 視圖裡面
Music 類:
直接報錯:
寫在 類裡面也 在函數里用 self.pool 調用也不行,也是相同的錯誤。
最後 把 pool = Pool 直接寫在 search 函數裡面,奇跡出現了:
前台也能顯示搜索的音樂結果了
總結一點,進程這個東西,最好 寫在 直接運行的函數裡面,而不是 一個函數跳來跳去。因為最後可能 是在子進程的子進程運行的,這是不許的,會報錯。
還有一點,多進程運行的函數對象,不能是 lambda 函數。也許lambda 虛擬,在內存??
使用 pool.map 子進程 函數報錯,導致整個 pool 掛了:
參考: https://blog.csdn.net/hedongho/article/details/79139606
主要你要,對函數內部捕獲錯誤,而不能讓異常拋出就可以了。
關於map 傳多個函數參數
我一開始,就是正常思維,多個參數,搞個元祖,讓參數一一對應不就行了:
報錯:
參考:
https://blog.csdn.net/qq_15969343/article/details/84672527
普通的 process 當讓可以穿多個參數,map 卻不知道咋傳的。
apply_async 和map 一樣,不知道咋傳的。
最簡單的方法:
使用 starmap 而不是 map
結果:
子進程結束
1.8399453163146973
成功拿到結果了
關於map 和 starmap 不同的地方看源碼:
關於apply_async() ,我沒找到多參數的方法,大不了用 一個迭代的 starmap 實現。哈哈
關於 上面源碼裡面有 itertools.starmap
itertools 用法參考:
https://docs.python.org/zh-cn/3/library/itertools.html#itertool-functions
有個問題,多進程最好不要使用全部的 cpu , 因為這樣可能影響其他任務,所以 在進程池 添加 process 參數 指定,cpu 個數:
上面就是預留了 一個cpu 干其他事的
後面直接使用 Queue 遇到這個問題:
解決:
Manager().Queue() 代替 Queue()
因為 queue.get() 是堵塞型的,所以可以提前判斷是不是 空的,以免堵塞進程。比如下面這樣:
使用 queue.empty() 空為True
㈢ python中更優雅的記錄日誌
在以往我們使用日誌,更多的是使用 python 自帶的 logging 模塊態大迅,它可以設置錯誤等級、輸出方式等。
但使用方式相對比較復雜,想要更好的使用需要如 log4net 一樣單獨配置,這在 python 中感覺不是很優雅。
下面介紹一個 python 庫: loguru 。 guru 是印度語中大師的意思, loguru 直譯就是「日誌大師」。
如圖 logging 一樣, loguru 也有定義日誌等級。不同的日誌等級,輸出效果也不一樣(默認的等級由低到高是 DEBUG 、 INFO 、 WARNING 、 ERROR 、 CRITICAL ,也可以自己使用 level 函數定義)。
類似 logging 中的 logger.addHandler ,loguru統一使用 add 函數來管理格式、文件輸出、過濾等操作,它提供了許多參數來實現 logger.addHandler 中的配置更加簡單方便。
其中 sink 是最重要的參數,可以傳入不同的數據類型。傳入文件路徑、文件句柄、 sys.stderr 、甚至 logging 模塊的 Handler 如 FileHandler 、 StreamHandler 等,這樣就可以快速實現自定義的 Handler 配置。
通過給 remove 方法傳遞 add 方法返回的對象, 可以帆此刪除 add 方仿槐法添加的 sink ,這里的 remove 並不是刪除 test2.log 文件,而是停止向該文件輸出日誌,需要需要繼續記錄日誌則需要重新 add 日誌文件。
用 rotation 、 retention 、 compression 進行日誌窗口、更新、壓縮管理。
支持控制台輸出添加顏色, 除了基礎色, loguru 甚至允許16進制、RGB格式的顏色值和加粗、下劃線等樣式。
使用裝飾器 @logger.catch 可以和 logging 一樣使用 logger.exception 函數來記錄異常信息。
使用 exception 方法輸出的異常信息包含堆棧信息和當前變數的值,方便問題定位。
使用 serialize 可以將日誌轉換為 JSON 格式, enqueue 可以保證多線程、多進程安全。
修改時間格式。
㈣ 在 python 項目中如何記錄日誌
寫本文的目的是我在寫 python 項目的時候需要記錄日誌,我忘記怎麼處理了,每次碼逗豎都需要去網上查一遍, 好記性不如爛筆頭 , 這里把查閱的內容記錄下來,方便以後查找。
python 項目中記錄日誌,可以使用 logging 模塊,logging 模塊定義的函數和類為應用程序和指歲庫的開發實現了一個靈活的事件日誌系統。logging 模塊是Python的一個標准庫模塊,由標准庫模塊提供日誌記錄API的關鍵好處是所有Python模塊都可以使用這個日誌記錄功能。所以,你的應用日誌可以將你自己的日誌信息與來自第三方模塊遲大的信息整合起來。
在 __init__.py 文件中做如下配置:
控制台輸出日誌如下:
參考文檔
https://www.cnblogs.com/yyds/p/6901864.html
㈤ python多進程存數據不改變順序
你的問題太模糊了。我先從常見的錯誤說,然後再從相對從大的方面來描述。
進程掛掉可能是什麼原因?
一方面是程序可能有邏輯錯誤,導致了諸如下標越界,數據異常等問題。
另一方面,可能是系統原因,比如代碼質量一般,系統資源消耗厲害,進程退出。
還有可能是上下游服務問題,比如 MySQL 等服務異常,上游 API 異常,配置錯誤。
簡單說了下各種可能的異常,但對一個項目而言,天下問題千千萬,不可能有一條完美的准則。
遇到問題,通常要從幾步出發,從排查出錯原因、尋找解決方案和如何預防出發。
排查錯誤的原因,這是最重要的一步,只有對症下葯才能解決問題。
首先確認下是否搏旅容易復現的問題,容易復現的話,通常會給出錯誤信息。然後,我們只要在開發時調試下就好了,常見的調試方法有 print 列印來觀察問題,或者是使用一些 debug 調試工具,我相信你會用。print 的特點是簡單好用,但每次都有修改代碼,比較繁瑣。而調試工具就比較方便,看的信息會比較全,一些 IDE 都集成了調試工具。
對於不容易復現的問題,可以通過記錄日誌的方式排查。有人會說,記錄日誌是資源消耗,曾經我也怎麼想過。但對於現在的硬體配置而已,記錄日誌的成本是非常小的,一個好像的項目肯定是集成日誌的,不然就太 low 逼了,我可不敢用。
尋找解決方案,這一步需要基於前面診斷出的結果進行排查。
一個簡單的案例。比如,提示 MySQL 連接數過多,什麼情況可能導致這個問題?是 MySQL 配置的連接數本身就很少,還是程序設計不合理導致連接無法正確復用,亦或是業務量真的大了,當前最大連塵巧接數無法承受呢。每種情況的處理方式都不同。
如何預防問題,我主要想學詳細的日誌,和增加一些恢復機制。
詳細的日誌就不說了,異常要記得捕獲,並且記錄發生異常的原因,這一步對排查問題非常有幫助。
另外,不是所派銀鍵有的異常都應該立刻退出進程,如果不是一些非常嚴重的錯誤,通過日誌提示下,程序還可以繼續工作。不過,有些異常還是要做一些修復處理,比如資料庫連接斷開,可嘗試重新連接,上游系統服務異常,可以執行多次調用,而不是直接退出。
就簡單說這么多吧!如果有些地方講的不是很好,見諒。歡迎評論補充,謝謝!
㈥ Loguru:Python 日誌終極解決方案
日誌的作用非常重要,日誌可以記錄用戶的操作、程序的異常,還可以為數據分析提供依據,日誌的存在意義就是為了能夠在程序在運行過程中記錄錯誤,方便維護和調試,能夠快速定位出錯的地方,減少維護成本。每個程序員都應該知道,不是為了記錄日誌而記錄日誌,日誌也不是隨意記的。要實現能夠只通過日誌文件還原整個程序執行的過程,達到能透明地看到程序里執行情況,每個線程、每個過程到底執行到哪的目的。日誌就像飛機的黑匣子一樣,應當能夠復原異常的整個現場乃至細節!
最常見的是把輸出函數 print() 當作日誌記錄的方式,直接列印各種提示信息,常見於個人練習項目里,通常是懶得單獨配置日誌,而且項目太小不需要日誌信息,不需要上線,不需要持續運行,完整的項目不推薦直接列印日誌信息,現實中也幾乎沒有人這么做。
我們可以在不少小項目裡面看到作者自己寫了一個日誌模板,通常利用 print() 或者 sys.stdout 稍微封裝一下即可實現簡單的日誌輸出,這里的 sys.stdout 是 Python 中的標准輸出流, print() 函數是對 sys.stdout 的高級封裝,當我們在 Python 中列印對象調用 print(obj) 時候,事實上是調用了 sys.stdout.write(obj+'\n') , print() 將內容列印到了控制台,然後追加了一個換行符 \n 。
自寫日誌模板適合比較小的項目,可以按照自己的喜好編寫模板,不需要太多復雜配置,方便快捷,但是這種記錄日誌的方式並不是很規范,有可能你自己覺得閱讀體驗不錯,但是別人在接觸你的項目的時候往往需要花費一定的時間去學習日誌的邏輯、格式、輸出方式等,比較大的項目同樣不推薦這種方法。
一個簡單的自寫日誌模板舉例:
日誌模板 log.py:
調用日誌模塊:
日誌輸出:
在一個完整的項目中,大多數人都會引入專門的日誌記錄庫,而 Python 自帶的標准庫 logging 就是專門為日誌記錄而生的,logging 模塊定義的函數和類為應用程序和庫的開發實現了一個靈活的事件日誌系統。由標准庫模塊提供日誌記錄 API 的關鍵好處是所有 Python 模塊都可以使用這個日誌記錄功能。所以,你的應用日誌可以將你自己的日誌信息與來自第三方模塊的信息整合起來。
logging 模塊雖然強大,但是其配置也是比較繁瑣的,在大型項目中通常需要單獨初始化日誌、配置日誌格式等等,K哥在日常使用中通常都會對 logging 做如下的封裝寫法,使日誌可以按天保存,保留15天的日誌,可以配置是否輸出到控制台和文件,如下所示:
輸出日誌:
它在控制台中是這樣的:
當然,如果你不需要很復雜的功能,希望簡潔一點,僅僅需要在控制台輸出一下日誌的話,也可以只進行簡單的配置:
對於 logging 模塊,即便是簡單的使用,也需要自己定義格式,這里介紹一個更加優雅、高效、簡潔的第三方模塊:loguru,官方的介紹是:Loguru is a library which aims to bring enjoyable logging in Python. Loguru 旨在為 Python 帶來愉快的日誌記錄。這里引用官方的一個 GIF 來快速演示其功能:
Loguru 僅支持 Python 3.5 及以上的版本,使用 pip 安裝即可:
Loguru 的主要概念是只有一個:logger
控制台輸出:
可以看到不需要手動設置,Loguru 會提前配置一些基礎信息,自動輸出時間、日誌級別、模塊名、行號等信息,而且根據等級的不同,還自動設置了不同的顏色,方便觀察,真正做到了開箱即用!
如果想自定義日誌級別,自定義日誌格式,保存日誌到文件該怎麼辦?與 logging 模塊不同,不需要 Handler,不需要 Formatter,只需要一個 add() 函數就可以了,例如我們想把日誌儲存到文件:
我們不需要像 logging 模塊一樣再聲明一個 FileHandler 了,就一行 add() 語句搞定,運行之後會發現目錄下 test.log 裡面同樣出現了剛剛控制台輸出的 debug 信息。
與 add() 語句相反, remove() 語句可以刪除我們添加的配置:
此時控制台會輸出兩條 debug 信息:
而 test.log 日誌文件裡面只有一條 debug 信息,原因就在於我們在第二條 debug 語句之前使用了 remove() 語句。
Loguru 對輸出到文件的配置有非常強大的支持,比如支持輸出到多個文件,分級別分別輸出,過大創建新文件,過久自動刪除等等。 下面我們來詳細看一下 add() 語句的詳細參數:
基本語法:
基本參數釋義:
當且僅當 sink 是協程函數時,以下參數適用:
當且僅當 sink 是文件路徑時,以下參數適用:
這么多參數可以見識到 add() 函數的強大之處,僅僅一個函數就能實現 logging 模塊的諸多功能,接下來介紹幾個比較常用的方法。
add() 函數的 rotation 參數,可以實現按照固定時間創建新的日誌文件,比如設置每天 0 點新創建一個 log 文件:
設置超過 500 MB 新創建一個 log 文件:
設置每隔一個周新創建一個 log 文件:
add() 函數的 retention 參數,可以設置日誌的最長保留時間,比如設置日誌文件最長保留 15 天:
設置日誌文件最多保留 10 個:
也可以是一個 datetime.timedelta 對象,比如設置日誌文件最多保留 5 個小時:
add() 函數的 compression 參數,可以配置日誌文件的壓縮格式,這樣可以更加節省存儲空間,比如設置使用 zip 文件格式保存:
其格式支持: gz 、 bz2 、 xz 、 lzma 、 tar 、 tar.gz 、 tar.bz2 、 tar.xz
Loguru 在輸出 log 的時候還提供了非常友好的字元串格式化功能,相當於 str.format() :
輸出:
在 Loguru 里可以直接使用它提供的裝飾器就可以直接進行異常捕獲,而且得到的日誌是無比詳細的:
日誌輸出:
在控制台的輸出是這樣的:
相比 Logging,Loguru 無論是在配置方面、日誌輸出樣式還是異常追蹤,都遠優於 Logging,使用 Loguru 無疑能提升開發人員效率。本文僅介紹了一些常用的方法,想要詳細了解可參考 Loguru 官方文檔 或關注 Loguru GitHub 。
㈦ 如何使用Python實現多進程編程
1.Process
創建進程的類:Process([group[,target[,name[,args[,kwargs]]]]]),target表示調用對象,args表示調用對象的位置參數元組。kwargs表示調用對象的字典。name為別名。group實質上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()啟動某個進程。
屬性:authkey、daemon(要通過start()設置)、exitcode(進程在運行時為None、如果為–N,表示被信號N結束)、name、pid。其中daemon是父進程終止後自動終止,且自己不能產生新進程,必須在start()之前設置。
例1.1:創建函數並將其作為單個進程
importmultiprocessing
importtime
defworker(interval):
n=5
whilen>0:
print("Thetimeis{0}".format(time.ctime()))
time.sleep(interval)
n-=1
if__name__=="__main__":
p=multiprocessing.Process(target=worker,args=(3,))
p.start()
print"p.pid:",p.pid
print"p.name:",p.name
print"p.is_alive:",p.is_alive()
結果
12345678p.pid:8736p.name:Process-1p.is_alive:TrueThetimeisTueApr2120:55:122015ThetimeisTueApr2120:55:152015ThetimeisTueApr2120:55:182015ThetimeisTueApr2120:55:212015ThetimeisTueApr2120:55:242015
例1.2:創建函數並將其作為多個進程
importmultiprocessing
importtime
defworker_1(interval):
print"worker_1"
time.sleep(interval)
print"endworker_1"
defworker_2(interval):
print"worker_2"
time.sleep(interval)
print"endworker_2"
defworker_3(interval):
print"worker_3"
time.sleep(interval)
print"endworker_3"
if__name__=="__main__":
p1=multiprocessing.Process(target=worker_1,args=(2,))
p2=multiprocessing.Process(target=worker_2,args=(3,))
p3=multiprocessing.Process(target=worker_3,args=(4,))
p1.start()
p2.start()
p3.start()
print("ThenumberofCPUis:"+str(multiprocessing.cpu_count()))
forpinmultiprocessing.active_children():
print("childp.name:"+p.name+" p.id"+str(p.pid))
print"END!!!!!!!!!!!!!!!!!"
結果
1234567891011ThenumberofCPUis:4childp.name:Process-3p.id7992childp.name:Process-2p.id4204childp.name:Process-1p.id6380END!!!!!!!!!!!!!!!!!worker_1worker_3worker_2endworker_1endworker_2endworker_3
例1.3:將進程定義為類
importmultiprocessing
importtime
classClockProcess(multiprocessing.Process):
def__init__(self,interval):
multiprocessing.Process.__init__(self)
self.interval=interval
defrun(self):
n=5
whilen>0:
print("thetimeis{0}".format(time.ctime()))
time.sleep(self.interval)
n-=1
if__name__=='__main__':
p=ClockProcess(3)
p.start()
註:進程p調用start()時,自動調用run()
結果
12345thetimeisTueApr2120:31:302015thetimeisTueApr2120:31:332015thetimeisTueApr2120:31:362015thetimeisTueApr2120:31:392015thetimeisTueApr2120:31:422015
㈧ Python日誌—Python日誌模塊logging介紹
從事與軟體相關工作的人,應該都聽過「日誌」一詞。
日誌就是跟蹤軟體運行時事件的方法,為了能夠在程序運行過程中記錄錯誤。
通過日誌記錄程序的運行,方便我們查詢信息,以便追蹤問題、進行維護和調試、還是數據分析。
並且各編程語言都形成了各自的日誌體系和相應的框架。
日誌的作用總結:
首先我們要樹立一個觀點,那就是「不是為了記錄日誌而記錄日誌,日誌也不是隨意記的」。要實現能夠只通過日誌文件還原整個程序執行的過程,達到能透明地看到程序里執行情況,每個線程每個過程到底執行結果的目的。日誌就像飛機的黑匣子一樣,應當能夠復原異常的整個現場乃至細節。
在項目中,日誌這個功能非常重要,我們要重視起來。
在Python中,使用logging模塊來進行日誌的處理。
logging是Python的內置模塊,主要用於將日誌信息進行格式化內容輸出,可將格式化內容輸出到文件,也可輸出到屏幕。
我們在開發過程中,常用print()函數來進行調試,但是在實際應用的部署時,我們要將日誌信息輸出到文件中,方便後續查找以及備份。
在我們使用日誌管理時,我們也可以將日誌格式化成Json對象轉存到ELK中方便圖形化查看及管理。
logging模塊將日誌系統從高向低依次定義了四個類,分別是logger(日誌器)、handler(處理器)、filter(過濾器)和formatter(格式器)。其中由日誌器生成的實例將接管原本日誌記錄函數logging.log的功能。
說明:
我們先來思考下下面的兩個問題:
在軟體開發階段或部署開發環境時,為了盡可能詳細的查看應用程序的運行狀態來保證上線後的穩定性,我們可能需要把該應用程序所有的運行日誌全部記錄下來進行分析,這是非常耗費機器性能的。
當應用程序正式發布或在生產環境部署應用程序時,我們通常只需要記錄應用程序的異常信息、錯誤信息等,這樣既可以減小伺服器的I/O壓力,也可以避免我們在排查故障時被淹沒在日誌的海洋里。
那麼怎樣才能在不改動應用程序代碼的情況下,根據事件的重要性或者稱之為等級,實現在不同的環境中,記錄不同詳細程度的日誌呢?
這就是日誌等級的作用了,我們通過配置文件指定我們需要的日誌等級就可以了。
說明:
總結:
開發應用程序時或部署開發環境時,可以使用DEBUG或INFO級別的日誌獲取盡可能詳細的日誌信息,可以方便進行開發或部署調試。 應用上線或部署生產環境時,應用使用WARNING或ERROR或CRITICAL級別的日誌,來降低機器的I/O壓力和提高獲取錯誤日誌信息的效率。 日誌級別的指定通常都是在應用程序的配置文件中進行指定的。 不同的應用程序所定義的日誌等級會有所差別,根據實際需求來決定。
㈨ python 多線程logger問題
因為logging是threadsafe的,但不是process-safe(應該沒有這個詞兒,只是為了便於理解)的。這段代碼就是多個進程共同操作一個日誌文件。這種情況下,logging的行為就很難說了。
我測試了一下,日誌中大概幾百行。而且,可以看到一些順序錯亂現象:
Fri, 08 Aug 2014 01:19:38 logging_in_multithread.py[line:40] theadWorking ERROR 2
FFri, 08 Aug 2014 01:19:36 logging_in_multithread.py[line:40] theadWorking ERROR 11(注意這里的FFri)
把代碼這樣改:
fornuminrange(processNum):
p=Process(target=processWorking,args=('2',))
processs.append(p)
p.start()
p.join()
還有其他方法,比如:為logging實現一個FileHandler,以使logging在multiple process的環境下也能正常工作。這是我從網上了解到的做法,自己還沒實踐過。
Python Manual中logging Cookbook中有這么一段話:
Logging to a single file from multiple processes
Although logging is thread-safe, and logging to a single file from multiple threads in a single process is supported, logging to a single file from multiple processes is not supported, because there is no standard way to serialize access to a single file across multiple processes in Python. If you need to log to a single file from multiple processes, one way of doing this is to have all the processes log to a SocketHandler, and have a separate process which implements a socket server which reads from the socket and logs to file. (If you prefer, you can dedicate one thread in one of the existing processes to perform this function.)
這段話中也提出了另外一種解決方案。
㈩ logging:多線程調試時用來代替print和單步調試
當你要寫多線程項目時,不免要調試錯誤,要debug。
一般debug的工具就是列印函數print, 調試工具gdb進行單步調試,但是多線程時,單步調試就很雞肋了,這時就需要列印日誌了
沒錯,列印日誌無疑是調試多線程工程的高效工具了。
在python中開發,就要用到logging日誌庫了
logging庫已經封裝好日誌需要的基本功能,能夠實現在文件里,在命令行等寫日誌
還能輸出日誌信息的類型,如debug,warning,error等
細節在這里有所介紹:
https://docs.python.org/3/howto/logging.html
文本介紹一下,第一次使用logging時,要熟悉logging時,需要用一個非常簡單的例子
先看一個最簡單的隱絕例子:
運行以上代碼:
printed out on the console. The INFO message doesn』t appear because the default level is WARNING . The printed message includes the indication of the level and the description of the event provided in the logging call, i.e. 『Watch out!』. Don』t worry about the 『root』 part for now: it will be explained later. The actual output can be formatted quite flexibly if you need that; formatting options will also be explained later.
輸出結果為:
如果你想每次都在一個新的日誌文件中寫日誌,那麼使用filemode='w'參數:
分別在main 函數里,在mylib.py里寫日誌
運行後的輸出:
以上代碼會顯示:
該 format參數的值
以上代碼會顯示:
還是該format:
以上代碼會輸出
如果想自己設定時間的格式:
會這樣顯示:
logging庫中用了模塊化的思路,把日誌的整體功能用了4個基本的模塊來完成:
Loggers,Handlers,Filters,Formatters
其中,Handlers,主要配置將信息寫到命令行,還是寫到文件里。
Filters,是對信息本身的過濾,決定那些信息不寫,那些信息寫。
Formatters決定信息輸出的格式。比如是否輸出時間,是否輸出logger本身的名字等,決定那些信息在前,那些信息在後等。
Logger就是Handler,Filter,Formatter配置的一個日誌對象了。
下面我們逐個說一下這4個類:
它有三個功能,1 提供分級日誌的輸出,比如 WARNING,ERROR,INFO等不同等級。2. 它可以決定哪些信息輸歷頃出,哪些信息不輸出。3 它可以將一條信息發給命令行和文件,可以把一條信息發給多個handler去處理。
logger 最常用的成員方法大概分兩類:配置和信息發送
以下是常用的配置函數:
Logger.setLevel() 設定信息記錄的等級。如果一條信息的等級比我們設定的低,那麼就不對此條信息進行處理。信息一共分為:DEBUG,INFO,WARNING,ERROR,CRITICAL 五個級別,其中DEBUG是最低的等級,CRITICAL是最高的等級。
Logger.addHandler() 和 Logger.removeHandler(),添加或者刪除信息處理器Handler。這個信息處理器就是定義了將日誌寫入命令行,還是寫入文件,或者寫入郵件等。
Logger.addFilter() 和 Logger.removeFilter() .添加或者刪除信息過濾器Filter。這個信息過濾器,決定了哪些信息不顯示。
可以看到,信息過肢攜陸濾器,信息處理器,信息等級共同配置了logger.
並不是每個logger你都需要去配置一遍,你可以利用logger的繼承機制,只配置父logger.
一旦配置好logger之後,就可以用以下函數來在你自己代碼的任意位置來記錄日誌了。
Logger.debug(),Logger.info(),Logger.warning(),Logger.error()和Logger.critical.這些函數的功能都是建立了日誌記錄信息,不同的是,函數名字就代表了其建立的日誌信息的等級。
Logger.exeception()建立一個與Logger.error()比較相似的信息。但是Logger.exception()是放入一個追蹤盞裡面的。所以,只有在exception handler處理器中,才能使用它。
Logger.log()發送一個LOG 等級的信息,使用LOG等級的信息稍微繁瑣些,因為使用LOG等級可以自定義等級。
getLogger() 返回一個logger的引用,如果指定了名字,那麼返回特定名字對應的logger,如果沒有指定名字,那就返回一個名字為root的loger的引用名字root,或者你指定的名字是一種級連結構。用同樣的名字去調用getLogger(),會得到同樣的值。logger的名字也決定了logger在樹結構中的層級。例如:這里有一個logger的名字為foo,那麼foo.bar,foo.bar.baz,還有foo.bam都是foo的子孫,logger還有一個有效等級level的概念。這個有效等級其實就是決定debug,info,warning等不同類型的消息是否進行記錄。因為logger本身有了父子那樣的繼承關系,所以有效等級level也是可以繼承的。如果子logger沒有設定了自身的level,那麼就把父logger的level繼承過來使用。如果子logger本身設定了level,就用自身這個level.如果父logger仍然沒有設定level,那就看父logger的父logger,一直這么追述下去,就會追述到root上,所以,我們必須給root設定一個level,或者默認一個level,方便root的子logger去繼承。我們給root設定的默認levle為WARNING.如果有些日誌的level,相比我們設定的WARNING低,那麼它就不會被傳遞給Handler去處理,就不會被列印出來,或者記錄進日誌文件。另外logger的屬性也是可繼承的,所以就只配置一下root logger即可,沒有root logger時,只用配置一個相對的那個根logger就行了
handler 信息處理器是負責信息分發給不同的目的地的,這個目的地可能是命令行,也可能是文件,或者郵件。分發時,同樣要檢查信息本身的等級severity.一個logger可以用addHandler()函數添加0個或者多個handlers。比如有這樣一個場景,希望發送所有的log等級的信息到一個log文件內。所有的等級為錯誤的信息到stdout 標准輸出上,發送所有的critical信息到郵件上。這樣的場景需要3個不同的處理器,每個處理器負責發送相應等級的信息到相應的目的地。
標准庫里包含一些處理器類型,本教程主要使用StreamHandler 和 FileHandler兩種信息處理器。
處理器中的成員函數非常少,我們用來配置處理器的成員函數大概有這個幾個:
setLevel(),用來設置處理器處理的信息等級。注意到logger中有setLevel(),而處理器中也有setLevel(),也就是說,logger把信息通過信息等級過濾一遍後,logger內的處理器需要根據處理器自身的功能設定,再根據信息等級來過濾一遍。
setFormatter() 為處理器設定一種信心記錄的格式Formatter
addFilter() removeFilter()函數添加信息過濾器或者刪除信息過濾器的函數。
到這里,我們發現,logger類中,將信息發送到不同的目的地就依賴Handlers實現的,
所有Handler就需要用Formatter和Filter來配置一下。接下來,我們看看Formatter和Filter.
它決定了信息顯示的順序,結構和內容。可以直接操作formatter的相關類,也可以自己繼承 formatter類,去完成自己的設定。formatter的構造函數需要3個可選的參數:字元串格式的信息,日期,一個符號。
logging.Formatter.__init__(fmt=None,datafmt=None,style='%')
如果這里沒有設定參數,就使用默認參數。對於fmt來說,就顯示原來信息,對於datafmt來說,就以年-月-日 時:分:秒的合適顯示。
下面的fmt就定義了一個按照 時間-信息等級-信息本身 來記錄日誌的方法:
這里有三種方法:
寫代碼配置logging的例子:
運行結果為:
寫配置文件配置logging的例子:
代碼中所需要的配置文件ogging.conf的具體內容為 :
以上代碼的輸出為:
顯然,配置文件修改起來相對容易很多。
還有更方便的配置方式,使用字典來配置:
運行結果與上面一樣。
到這里,基本完成了logging庫的比較常用的使用方式。
比如在什麼情況下,需要使用一個什麼都不做的處理器NullHandler(),
還有關於信息等級的更詳細的解釋:
以及如何自定義信息等級。
還有關於異常的處理
還有關於信息的,信息都是字元串的。不過你也可以直接把某個類作為信息拋出來,因為類會自動調用其__str__()函數,返回一個類的字元串回來。
還有就是優化性能的一些小技巧
這里就編寫expensive_func1 和 expensive_func2 來完成設定數據格式。
以下表格也顯示了如何收集運行log的代碼文件信息,線程信息,進程信息,處理器信息
以及如何操作才能收集代碼文件信息,線程信息,進程信息,以及處理器信息。
我們一般會使用收集代碼文件,線程,進程等信息,因為這樣大大方便了多線程多進程工程的調試。