logging模塊
logging 模塊是一個較龐大的模塊。具有較完備的日志體系。
主要分為:主體 Logger - 處理器 - 格式器logging 為 python 內置模塊,無需安裝。
導入方式: import logging 即可
日志等級排序 (弱 -> 強)
DEBUG < INFO < WARNING < ERROR < FATAL
DEBUG : 開發調試的一些信息(print調試。。。)
INFO: 程序運行過程的重要信息(不宜過多)
WARNING: 不影響程序運行的小問題,警告一下。記錄下來以備以后解決。
ERROR: 影響程序, 有點嚴重。需要處理, 不然程序 (可能,可能)就掛了。
FATAL: 嚴重影響程序,立刻重新排查,修改代碼吧。
logging體系組件
常用分為: (由外到里的包含關系)
-
Logger (主體日志)
最大的容器,里面裝 Handler -
Handler (處理器類)
里面裝 Formatter -
Formatter (格式器類)
里面寫一些打印信息的格式語法
為了方便說明,接下來,我會把上面的組件“由里到外”講解。
Formatter(格式器類)
格式器:用來定義一些打印信息的字符串格式化的語法。
初始化一個 格式器:
fmt = '[%(asctime)s] [%(filename)s: %(lineno)d] [%(levelname)s] => %(message)s' # 格式
console_formatter = logging.Formatter(fmt=fmt) # 實例化格式器,并把格式傳進來
理解方式:
-
我相信你只有 “格式”這行 看不明白,這格式你可以隨便搭配的, 見下方官方文檔有參數大全:
官檔格式大全:https://docs.python.org/3.7/l... -
打開官檔,你會看見表格的第二列 Format。 里面的格式直接原封不動復制過來即可 eg: %(asctime)s
然后,你把這些格式用字符串拼接成自己喜歡的符號格式即可, eg: "日期為 => %(asctime)s" -
也許你會疑惑,為什么這種 %(..)s 的格式能被識別。 而不是被當作原始字符串???
注意第二行代碼, fmt格式字符串只是 Formatter() 的參數,它里面會自動被解析的。這你就別操心了。 -
我把上例結果貼一下,你可能會看明白些:
>>> [2019-09-10 18:23:19,347] [logging11.py: 15] [WARNING] => 哈哈哈 asctime 是日期 filename 是文件名 lineno 是代碼行 levelname 是日志等級名 (就是上面說的 INFO WARNING ERROR之類的) message 是 你要打印的日志信息 (下面會講到,這里先小小埋一個點)
Handler(處理器類)
處理器:用來裝載上面說的 “格式器”,并處理日志 (處理器有很多種,按需選1個即可,下面說2種常用的):
初始化一個 “流處理器” (比較常用):
handler = logging.StreamHandler()
或初始化一個 “文件處理器” (源碼明確寫了, 它繼承的是 上面的 “流處理器”。通常用來日志持久化):
handler = logging.FileHandler('mylog.log', mode='a', encoding='utf-8')
# 不必解釋了吧。 這API語法很熟悉了吧。 這不就是我們常用的文件 open 語法么。。。。
裝載“格式器” (差點忘了吧。實例化的格式器,還沒用呢, 就是在這里裝載)
handler.setFormatter(fmt=file_formatter)
注意: 雖然 handler對象就可以用 setLevel()設置日志等級,但我不推薦在這里設置。繼續往下看
Logger(主體日志類)
Logger: 用來裝載 “處理器的”。
實例化Logger有兩種方法:
方法1:(非共享式創建, 不推薦)
log= logging.Logger(name='my_log', level='INFO')
# name 是給 Logger 起的名
# level是 日志等級(注意要大寫), 開篇我們講到過, WARNING, INFO, ERROR 這些。
方法2:(Log池共享式創建, 推薦)
log = logging.getLogger(name='console')
# 有則取出,無則創建
# name如果不傳,則取出root Logger (root Logger是logging默認給我們提供的,我一般不用)
說一下這兩種方法的區別:
- 非共享式: 即為每次都需要重新創建。從0開始配置
-
Log池共享式: 從Log池取出索引來操作(就相當于函數傳索引操作)
你每對取出的Log做出配置時,都會映射保存更新到 Log池 中。
當下次(或其他文件, 當然是一個完整的程序)調用 getLogger() 取出的 log,就是之前我們配好的。
裝載 "處理器" :(差點忘了吧, 上面定義的 處理器,還沒用呢, 就是在這里用的):
log.addHandler(handler)
設置日志等級 (這步可忽略)
log.setLevel('ERROR')
其實上面我們實例化Logger的時候,我們就已經傳了一個 level參數,設置好了 日志等級。
所以 log.setLevel() 這個可以不設置 (包括前面提到,handler也有 setLevel)
handler.setLevel()
開始輸出日志信息,有以下日志等級相對應的API:
log.debug ("這是一條 調試 日志")
log.info ("這是一條 顯示主要信息 日志")
log.warning('這是一條 警告 日志')
log.error ("這是一條 錯誤 日志")
log.fatal ("這是一條 致命錯誤 日志")
### 回顧我們前面講的 Formatter 格式器
我們第一個講的就是格式器, 并說了一下常用格式。
其中有個 %(message)s, 它就是占位上面這些API里面的參數
eg: log.info('哈哈哈')
%(message)s格式 占位輸出的就是 哈哈哈
還有個 %(levelname)s,它就是占位上面這些API的方法名
eg: log.info('xxx')
%(levelname)s 格式占位輸出的就是 info
如果你對日志等級與日志的作用感到模糊,你一定要看我接下來的例子!!!!!!!!
開篇時我就提過: 日志等級排序(弱=>強) => (DEBUG < INFO < WARNING < ERROR < FATAL)你設置了一個日志等級 。那么你所用上面API對應的等級若“強于或等于” 此設定的等級,日志才會被處理
emmmmm, 如果沒聽懂,就當我放P了。。。 說的越正式,越不容易理解。 我們還是看下面的例子吧~~
日志等級理解的小例子:
log.setLevel('WARNING') 你看我們設置的日志等級是 WARNING
log.debug("這是一條 調試 日志")
# 這個 debug(), 你可以去開篇列的"日志等級排序"那里瞅一眼。
# debug 比 warning 弱, 所以 這條日志是 不會 被處理的。
# (白話理解:"我給的界限是warning, 你一個 debug等級太低了,問題不嚴重。不配被記錄。")
log.info("這是一條 顯示主要信息 日志")
# 同理, info 也比 warning 弱, 此條日志也 不會 被處理
log.warning('這是一條 警告 日志')
# warning == warning (我前面說了,強于 或 等于) 所以此條日志會被處理
log.error("這是一條 錯誤 日志")
# error 比 warning 強, 所以此條日志 會 被處理
log.fatal("這是一條 致命錯誤 日志")
# fatal 比 warning 強, 所以此條日志 會 被處理
# 再白話一下:"你給我的容忍程度是 warning, 而你的這條日志都致命錯誤了,我肯定處理你啊"
思考!上例我一直說一句話 “xxxxx, 此條日志才會被處理”。
那么這個“ 處理” ,到底是處理什么呢???
這時不妨回頭看看,上面講的 “處理器”, 嗯, 沒錯。 這些日志就是 “處理器” 處理的。
- 你要是定義一個流處理器(logging.StreamHandler), 它就會把日志輸出到終端。
- 你要是定義一個文件處理器(logging.FileHandler), 它就會自動把日志保存到文件中,做持久化
綜合案例:
業務需求如下(隨便舉個案例,不一定有用):
-
比 DEBUG(強), 但又比 WARNING(弱) ,(不包括 WARNING) 的這類日志,只輸出到終端。
這類日志輸出格式無要求 -
比 WARNING(強) (包括 WARNING )的這類日志, 輸出到 終端和文件 各一份。
這類日志輸出格式有要求,格式為: [日期] [所在文件名: 代碼所在行] [日志級別] => 日志內容
代碼如下(自己使用的話,封裝一下比較好):
import logging
# 日志等級排序(弱-> 強): DEBUG < INFO < WARNING < ERROR < FATAL
fmt = '[%(asctime)s] [%(filename)s: %(lineno)d] [%(levelname)s] => %(message)s' # 格式
file_formatter = logging.Formatter(fmt=fmt) # 定義格式器, 把格式塞進來
file_handler = logging.FileHandler('mylog.log', mode='a', encoding='utf-8') # 定義文件處理器
file_handler.setFormatter(fmt=file_formatter) # 給文件處理器設置 一個 格式器
file_handler.setLevel('WARNING') # 給此處理器設置 日志等級
console_handler = logging.StreamHandler() # 定義流處理器,用于輸出到終端
# StreamHandler未設置格式器,它會默認給你設置一個 %(message)s,即只有日志內容,沒有日期文件名等
console_handler.setLevel('DEBUG') # 給流處理器設置 日志等級
log = logging.getLogger(name='file_log') # log池中取出一個log(若沒有則新建)
log.addHandler(file_handler) # 添加一個文件處理器(格式化 輸出到 文件)
log.addHandler(console_handler) # 再添加一個流處理器(無格式 輸出到 終端)
log.info('我只會輸出到終端') # 因為 info只比 console_handler 設置的 DEBUG強
log.error('我既會輸出到終端, 又會輸出到文件')
# 因為error 比 console_handler 設置的 DEBUG 強, 同時 error 也比 file_handler 設置的 WARNING 強
運行結果:
終端輸出:
>> 我只會輸出到終端
我既會輸出到終端, 又會輸出到文件
mylog.log 文件中:
[2019-09-10 23:54:59,055] [logging11.py: 20] [ERROR] => 我既會輸出到終端, 又會輸出到文件
----------------------華麗分割線----------------
投機取巧方式 (不推薦,這里開始往后,可不看)
這種方式只方便了一點點,但不靈活。
前面我們花了好大力氣,
- 先是定義了一個 格式器
- 又是定義了一個 流處理器 和 文件處理器
- Logger池中,實例化一個 Logger
- 并且把他們各種拼裝, 設置日志等級。等操作 (雖然看起來很多。其實你捋通了。真的不復雜)
其實 logging體系中, 有一個默認初始的 Logger, 叫做 root Logger.
我們不需要實例化它,也不需要實例化"格式器", 也不需要實例化控制器。
一行API就可以使用它 (默認是輸出到終端的):
import logging
fmt = '[%(asctime)s] [%(filename)s: %(lineno)d] [%(levelname)s] => %(message)s' # 格式
logging.basicConfig( # 默認使用的就是 root Logger
level='DEBUG', # 設置日志等級為DEBUG
format=fmt, # 設置格式
)
logging.info('我只會輸出到終端')
運行結果:
>> [2019-09-11 00:15:55,219] [logging11.py: 30] [INFO] => 我只會輸出到終端
如果想輸出到文件,那么只需加 filename 和 filemode 兩個參數即可:
import logging
fmt = '[%(asctime)s] [%(filename)s: %(lineno)d] [%(levelname)s] => %(message)s' # 格式
logging.basicConfig(
level='DEBUG',
format=fmt,
filename='mylog.log', # 文件名
filemode='a' # 文件操作符
)
運行結果:
mylog.log文件:
[2019-09-11 00:20:22,396] [logging11.py: 32] [INFO] => ?????????????
但你發現沒,往文件里面輸出亂碼了, 用耳朵都能想出來,我們沒有配置 encoding。。。
但是,我告訴你, basicConfig() 是沒有 encoding參數的。 那咋整 ??
但它有個參數叫做 handlers,handlers熟悉吧,沒錯就是我們上面講的 "處理器", 復數說明可以傳多個
logging.basicConfig(
level='DEBUG',
format=fmt,
handlers=[ logging.FileHandler(filename='mylog.log',mode='a',encoding='utf-8') ]
# 看這里這個處理器的定義方法,和之前講過的一模一樣。在這里我們可以配 encoding
)
# 這樣就不會亂碼了
Note: 以上就是用 logging.baseConfig() 簡單日志實現
說了它是投機取巧,因為除了文件亂碼有問題之外, 它還欠缺靈活性。
比如你想想對不同級別的日志,用不同格式輸出出來。這時你單用basicConfig一行是搞不定的。
所以還是推薦用 getLogger()那套組合。
結束語
logging模塊其實還有很多很多功能:
過濾器:(其實還有個這個組件,但我沒用過,就沒說)
格式器:(前面給了官檔大全,里面還有根據進程、線程的(PID,TID, tName,pName)等格式來輸出日志。)
控制器:(我只說了 stream 和 file),其實還有很多,它們都在logging.handlers模塊下:
from logging.handlers import (
RotatingFileHandler, # 通過設置文件大小閾值,超出這個閾值,就會將日志轉存新文件
TimedRotatingFileHandler, # 設置時間間隔,每過這個間隔,就會將日志轉存新文件
HTTPHandler, # 通過HTTP協議將日志輸出到遠程服務器,(只支持 GET 和 POST)
SMTPHandler, # 通過SMTP協議,將日志輸出到遠程郵箱了
SocketHandler, # 通過TCP協議發送到遠程服務器。。。
DatagramHandler, # 通過UDP協議發送到遠程服務器。。。
QueueHandler, # 發到隊列中(如果想發RabbitMQ之類的,可以去github找別人寫的成品)
)
# 這些用法也很簡單,看官檔,或者用Pycharm ctrl+左鍵點進源碼,看一下__init__()參數實例化即可
# 實例化后,用 xxx.addHandler() 添加到 logger即可使用 (和前面講的 file和stream用法一樣)
還可做成各類型配置文件使用:https://docs.python.org/3/lib...
官檔案例大全:https://docs.python.org/3/how...
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
