根據線程調試腳本
Ⅰ 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的代碼文件信息,線程信息,進程信息,處理器信息
以及如何操作才能收集代碼文件信息,線程信息,進程信息,以及處理器信息。
我們一般會使用收集代碼文件,線程,進程等信息,因為這樣大大方便了多線程多進程工程的調試。