django-logging
一、日誌的相關概念
日誌是一種能夠追蹤某些軟件運行時所發生事件的方法。軟件開發人員能夠向他們的代碼中調用日誌記錄相關的方法來代表發生了某些事情。一個事件能夠用一個可包含可選變量數據的消息來描述。此外,事件也有重要性的概念,這個重要性也能夠被稱爲嚴重性級別(level)php
日誌的做用
經過log的分析,克以方便用戶瞭解系統軟件,和應用的運行狀況,若是你的log足夠豐富,也能夠分析出用戶的行爲,類型喜愛,地域分佈等等。python
若果一個應用的log同時也分了多個級別,那麼能夠很輕易的分析出應用的健康狀況,及時發線問題並快速定位問題點,彌補損失。nginx
簡單的來講 咱們能夠經過記錄和分析日誌 能夠了解一個系統或軟件的運行狀況是否正常,也能夠快速定位問題點。程序員
好比:django
運維的同窗在接收警報或個種問題反饋後 ,進行問題排查都會先看各類日誌,大部分問題都是能夠在日誌中找到答案。
開發同窗,能夠經過ide控制檯數出各類日誌進行調試。服務器
對於老運維或者有經驗的開發人員,能夠快速的經過日誌定位到問題的根源。可見,日誌的重要性不可小覷。日誌的做用能夠簡單總結爲如下3點:markdown
- 程序調試
- 瞭解軟件運行狀況,是否正常
- 應用出現故障快速定位
若是應用的日誌信息足夠詳細和豐富,還能夠用來作用戶行爲分析,如:分析用戶的操做行爲、類型洗好、地域分佈以及其它更多的信息,由此能夠實現改進業務、提升商業利益。網絡
日誌的等級
咱們先來思考下面的兩個問題:app
- 做爲開發人員,在開發應用程序的時候須要什麼日誌信息,應用程序上線以後須要什麼日誌信息
- 做爲運維人員,在部署開發環境須要什麼日誌信息,在部署生產環境以後須要什麼日誌信息
在軟件開發階段或部署開發環境時,爲了儘量詳細的查看應用程序的運行狀態來保證上線後的穩定性,咱們可能須要把該應用程序全部的運行日誌所有記錄下來進行分析,這是很是耗費機器性能的。當應用程序正式發佈或在生產環境部署應用程序時,咱們一般只須要記錄應用程序的異常信息、錯誤信息等,這樣既能夠減少服務器的I/O壓力,也能夠避免咱們在排查故障時被淹沒在日誌的海洋裏。那麼,怎樣才能在不改動應用程序代碼的狀況下實如今不一樣的環境記錄不一樣詳細程度的日誌呢?這就是日誌等級的做用了,咱們經過配置文件指定咱們須要的日誌等級就能夠了。運維
不一樣的應用程序所定義的日誌等級可能會有所差異,分別詳細點的包含一下幾個等級:
通常用的時候會一塊兒討論日誌級別的顏色。
詳細做用:https://www.infoq.cn/article/five-levels-of-logging
ALL 最低等級的,用於打開全部日誌記錄。
TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日誌級別,通常不會使用。
DEBUG 指出細粒度信息事件對調試應用程序是很是有幫助的,主要用於開發過程當中打印一些運行信息。
INFO 消息在粗粒度級別上突出強調應用程序的運行過程。打印一些你感興趣的或者重要的信息,這個能夠用於生產環境中輸出程序運行的一些重要信息,可是不能濫用,避免打印過多的日誌。
WARN 代表會出現潛在錯誤的情形,有些信息不是錯誤信息,可是也要給程序員的一些提示。
ERROR 指出雖然發生錯誤事件,但仍然不影響系統的繼續運行。打印錯誤和異常信息,若是不想輸出太多的日誌,可使用這個級別。
FATAL 指出每一個嚴重的錯誤事件將會致使應用程序的退出。這個級別比較高了。重大錯誤,這種級別你能夠直接中止程序了。
OFF 最高等級的,用於關閉全部日誌記錄。
日誌字段信息與日誌格式
一條日誌信息對應的是一個事件的發生,而一個事件一般須要包括如下幾個內容:
- 事件發生時間
- 事件發生位置
- 事件嚴重程度--也就是日誌級別
- 事件內容
出了上面這些 還能夠包括什麼其餘信息,進程id,進程名稱,線程id
說明:
輸出一條日誌時,日誌內容和日誌級別是須要開發人員明確指定的。對於而其它字段信息,只須要是否顯示在日誌中就能夠了。
日誌功能的實現
幾乎全部的語言都有日誌的相關功能,或者比較優秀的地三方庫
python就有一個標準庫logging模塊
二、logging模塊簡介
logging模塊定義的函數和類爲應用程序和庫的開發實現了一個靈活的事件日誌系統。logging模塊是Python的一個標準庫模塊,由標準庫模塊提供日誌記錄API的關鍵好處是全部Python模塊均可以使用這個日誌記錄功能。因此,你的應用日誌能夠將你本身的日誌信息與來自第三方模塊的信息整合起來。
logging模塊的日誌級別
logging模塊默認定義瞭如下幾個日誌等級,它容許開發人員自定義其餘日誌級別,可是這是不被推薦的,尤爲是在開發供別人使用的庫時,由於這會致使日誌級別的混亂。
不能修改日誌級別
級別 | 值 | 描述 |
---|---|---|
CRITICAL/FATAL | 50 | 關鍵錯誤/消息 |
ERROR | 40 | 錯誤 |
WARNING | 30 | 警告消息 |
INFO | 20 | 通知消息 |
DEBUG | 10 | 調試 |
NOTSET | 0 | 無級別 |
- 開飯應用程序或部署開發環境時
- 可使用debug或info級別的日誌獲取儘量詳細的日誌信息
- 應用上線或部署生產環境時
- 應該使用warning或error或critcal級別的日誌來下降機器的I/O壓力和提升獲取錯誤日誌信息效率。
- 日誌級別的日誌級別的指定一般是在配置文件進行指定。
當某個應用程序指定日誌級別後,應用程序會記錄全部日誌級別大於或者小於指定日誌級別的日誌信息,
而不只僅是指定日誌級別的日誌信息nginx、php等應用程序以及這裏要提升的python的logging模塊都是這樣的。一樣,logging模塊也能夠指定日誌記錄器的日誌級別,只有級別大於或等於該指定日誌級別的日誌記錄纔會被輸出,小於該等級的日誌記錄將會被丟棄。
logging模塊的使用方式介紹
logging 有兩種使用方式
- 第一種方式使用logging提供的模塊級別的函數
- 也就是對logging日誌系統相關類的封裝
- 第二種是使用Logging日誌系統的四大組件
logging模塊定義的模塊級別的經常使用函數
函數 | 說明 |
---|---|
logging.debug(msg, args, *kwargs) | 建立一條嚴重級別爲DEBUG的日誌記錄 |
logging.info(msg, args, *kwargs) | 建立一條嚴重級別爲INFO的日誌記錄 |
logging.warning(msg, args, *kwargs) | 建立一條嚴重級別爲WARNING的日誌記錄 |
logging.error(msg, args, *kwargs) | 建立一條嚴重級別爲ERROR的日誌記錄 |
logging.critical(msg, args, *kwargs) | 建立一條嚴重級別爲CRITICAL的日誌記錄 |
logging.log(level, args, *kwargs) | 建立一條嚴重級別爲level的日誌記錄 |
logging.basicConfig(**kwargs) | 對root logger進行一次性配置 |
其中logging.basicConfig(**kwargs)
函數用於指定「要記錄的日誌級別」、「日誌格式」、「日誌輸出位置」、「日誌文件的打開模式」等信息,其餘幾個都是用於記錄各個級別日誌的函數。
logging模塊的四大組件
組件 | 說明 |
---|---|
日誌記錄器 loggers | 提供應用程序代碼直接使用的接口 |
處理器 handlers | 用於將日誌記錄發送到指定的目的位置 |
過濾器 filters | 提供更細粒度的日誌過濾功能,用於決定哪些日誌記錄將會被輸出(其它的日誌記錄將會被忽略) |
格式化器formatters | 用於控制日誌信息的最終輸出格式 |
說明: logging模塊提供的模塊級別的那些函數實際上也是經過這幾個組件的相關實現類來記錄日誌的,只是在建立這些類的實例時設置了一些默認值
三、使用logging提供的模塊級別的函數記錄日誌
- 能夠經過logging模塊定製模塊級別的方法完成簡單日誌記錄
- 只有級別大於或等於日誌記錄器級別才能輸出,小於的會唄拋棄
最簡單的日誌輸出
先寫一個簡單不一樣的日誌級別
import logging logging.debug("debug log") #程序信息 logging.info("info log") # 用戶級別信息 logging.warning("warning log") # 警告信息 潛在報警 logging.error("error log") # 有錯但不影響運行 logging.fatal("fatal log") # 必須查看 能夠中止程序 # 問前兩個沒有輸出
也能夠
logging.log(logging.DEBUG,"debug log") logging.log(logging.INFO,"INFO log") logging.log(logging.WARNING,"WARNING log") logging.log(logging.ERROR,"ERROR log") logging.log(logging.FATAL,"FATAL log")
輸出結果:
WARNING:root:debug log ERROR:root:debug log CRITICAL:root:debug log
問題來了
問:爲何前兩條爲何沒有打印
這是由於logging模塊提供的日誌記錄函數的日誌級別WARNING(警告級別),所以WARNING級別大於它的error和fatal 輸出小於的就被丟棄了
問:打印出來的是什麼意識
由於:
WARNING:root:debug log # 日誌級別:日誌器名稱:日誌內容
爲什是這樣的呢 日誌器設置的日誌格式默認的是BASIC_FORMAT
BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s" # 源碼是460行
logging.basicCongfig()函數說明
這個方法用於logging日誌的的一些基本配置
方法以下:
logging.basicConfig(**kwargs)
該函數可接收的關鍵字參數以下:
參數名稱 | 描述 |
---|---|
filename | 指定日誌輸出目標文件的文件名,指定該設置項後日志信心就不會被輸出到控制檯了 |
filemode | 指定日誌文件的打開模式,默認爲'a'。須要注意的是,該選項要在filename指定時纔有效 |
format | 指定日誌格式字符串,即指定日誌輸出時所包含的字段信息以及它們的順序。logging模塊定義的格式字段下面會列出。 |
datefmt | 指定日期/時間格式。須要注意的是,該選項要在format中包含時間字段%(asctime)s時纔有效 |
level | 指定日誌器的日誌級別 |
stream | 指定日誌輸出目標stream,如sys.stdout、sys.stderr以及網絡stream。須要說明的是,stream和filename不能同時提供,不然會引起 ValueError 異常 |
style | Python 3.2中新添加的配置項。指定format格式字符串的風格,可取值爲'%'、'{'和'$',默認爲'%' |
handlers | Python 3.3中新添加的配置項。該選項若是被指定,它應該是一個建立了多個Handler的可迭代對象,這些handler將會被添加到root logger。須要說明的是:filename、stream和handlers這三個配置項只能有一個存在,不能同時出現2個或3個,不然會引起ValueError異常。 |
import logging import time import datetime log_format = " %(process)d -%(module)s - %(name)s - %(asctime)s PM - %(levelname)s - %(message)s" # 固定寫法 log_date = "%s"%datetime.datetime.now().date() # 定製時間 logging.basicConfig( filemode="a", # 文件打開模式 filename="test", # 文件名 format=log_format, # 格式化 level=logging.DEBUG, # 級別定製 datefmt=log_date, # 定製時間格式 ) logging.debug("debug log") #程序信息 logging.info("info log") # 用戶級別信息 logging.warning("warning log") # 警告信息 潛在報警 logging.error("error log") # 有錯但不影響運行 logging.fatal("fatal log") # 必須查看 能夠停 # 問前兩個沒有輸出
logging模塊定義的格式字符串字段
們來列舉一下logging模塊中定義好的能夠用於format格式字符串中字段有哪些:
字段/屬性名稱 | 使用格式 | 描述 |
---|---|---|
asctime | %(asctime)s | 日誌事件發生的時間--人類可讀時間,如:2003-07-08 16:49:45,896 |
created | %(created)f | 日誌事件發生的時間--時間戳,就是當時調用time.time()函數返回的值 |
relativeCreated | %(relativeCreated)d | 日誌事件發生的時間相對於logging模塊加載時間的相對毫秒數(目前還不知道幹嗎用的) |
msecs | %(msecs)d | 日誌事件發生事件的毫秒部分 |
levelname | %(levelname)s | 該日誌記錄的文字形式的日誌級別('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') |
levelno | %(levelno)s | 該日誌記錄的數字形式的日誌級別(10, 20, 30, 40, 50) |
name | %(name)s | 所使用的日誌器名稱,默認是'root',由於默認使用的是 rootLogger |
message | %(message)s | 日誌記錄的文本內容,經過 msg % args 計算獲得的 |
pathname | %(pathname)s | 調用日誌記錄函數的源碼文件的全路徑 |
filename | %(filename)s | pathname的文件名部分,包含文件後綴 |
module | %(module)s | filename的名稱部分,不包含後綴 |
lineno | %(lineno)d | 調用日誌記錄函數的源代碼所在的行號 |
funcName | %(funcName)s | 調用日誌記錄函數的函數名 |
process | %(process)d | 進程ID |
processName | %(processName)s | 進程名稱,Python 3.1新增 |
thread | %(thread)d | 線程ID |
threadName | %(thread)s | 線程名稱 |
通過配置的日誌輸出
先簡單配置下日誌器的日誌級別
logging.basicConfig(level=logging.DEBUG) logging.debug("This is a debug log.") logging.info("This is a info log.") logging.warning("This is a warning log.") logging.error("This is a error log.") logging.critical("This is a critical log.")
輸出結果:
DEBUG:root:This is a debug log. INFO:root:This is a info log. WARNING:root:This is a warning log. ERROR:root:This is a error log. CRITICAL:root:This is a critical log.
全部等級的日誌信息都被輸出了,說明配置生效了。
在配置日誌器日誌級別的基礎上,在配置下日誌輸出目標文件和日誌格式
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT) logging.debug("This is a debug log.") logging.info("This is a info log.") logging.warning("This is a warning log.") logging.error("This is a error log.") logging.critical("This is a critical log.")
此時咱們就發現已經輸出個人文件裏面去了
2017-05-08 14:29:53,783 - DEBUG - This is a debug log. 2017-05-08 14:29:53,784 - INFO - This is a info log. 2017-05-08 14:29:53,784 - WARNING - This is a warning log. 2017-05-08 14:29:53,784 - ERROR - This is a error log. 2017-05-08 14:29:53,784 - CRITICAL - This is a critical log.
坑
- logging.basicConfig()是一個簡單的配置工具,一次性的配置工具,也就是說只有在第一次該函數被調用的時候會氣做用 ,屢次調用並不會累加。
- logging.warning("%s is %s warning log."%("This",1))不能這樣用。只能做爲將變量數據做爲第二個參數*args的值進行傳遞logging.warning("%s is %d warning log.g","This",3)
- 日誌器(Logger)是有層級關係的,上面調用的logging模塊級別的函數所使用的日誌器是
RootLogger
類的實例,其名稱爲'root',它是處於日誌器層級關係最頂層的日誌器,且該實例是以單例模式存在的 - logging.debug(), logging.info()等方法的定義中,除了msg和args參數外,還有一個**kwargs參數。它們支持3個關鍵字參數:
exc_info, stack_info, extra
,下面對這幾個關鍵字參數做個說明。- exc_info: 其值爲布爾值,若是該參數的值設置爲True,則會將異常異常信息添加到日誌消息中。若是沒有異常信息則添加None到日誌信息中。
- stack_info: 其值也爲布爾值,默認值爲False。若是該參數的值設置爲True,棧信息將會被添加到日誌信息中。
- extra: 這是一個字典(dict)參數,它能夠用來自定義消息格式中所包含的字段,可是它的key不能與logging模塊定義的字段衝突
例子:
在日誌消息中添加exc_info和stack_info信息,並添加兩個自定義的字端 ip和user
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s" DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p" logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT) logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})
輸出結果:
05/08/2017 16:35:00 PM - WARNING - Tom[47.98.53.222] - Some one delete the log file. NoneType Stack (most recent call last): File "C:/Users/wader/PycharmProjects/LearnPython/day06/log.py", line 45, in <module> logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'47.98.53.222'})
四、logging模塊日誌流處理流程
在介紹logging模塊的高級用法以前,頗有必要對logging模塊所包含的重要組件以及其工做流程作個全面、簡要的介紹,這有助於咱們更好的理解咱們所寫的代碼(將會觸發什麼樣的操做)。
logging日誌模塊四大組件
在介紹logging模塊的日誌流處理流程以前,咱們先來介紹下logging模塊的四大組件:
組件名稱 | 對應類名 | 功能描述 |
---|---|---|
日誌器 | Logger | 提供了應用程序可一直使用的接口 |
處理器 | Handler | 將logger建立的日誌記錄發送到合適的目的輸出 |
過濾器 | Filter | 提供了更細粒度的控制工具來決定輸出哪條日誌記錄,丟棄哪條日誌記錄 |
格式器 | Formatter | 決定日誌記錄的最終輸出格式 |
Logging是經過這些組件來完成日誌處理的上面所使用的logging函數也是經過這幾個類來實現的
這些組件之間的關係描述:
- 日誌器(logger)須要經過處理器(handler)將日誌信息輸出到目標位置,如:文件、sys.stdout、網絡等;
- 不一樣的處理器(handler)能夠將日誌輸出到不一樣的位置;
- 日誌器(logger)能夠設置多個處理器(handler)將同一條日誌記錄輸出到不一樣的位置;
- 每一個處理器(handler)均可以設置本身的過濾器(filter)實現日誌過濾,從而只保留感興趣的日誌;
- 每一個處理器(handler)均可以設置本身的格式器(formatter)實現同一條日誌以不一樣的格式輸出到不一樣的地方。
簡單的說日誌器是入口,真正幹活的是處理器,處理器還可經過過濾器和格式器對要輸出的日誌作過濾和格式化操做。
logging日誌模塊相關類及其經常使用方法介紹
下面介紹下與logging四大組件相關的類:Logger, Handler, Filter, Formatt
Logger類
logger對象有3個任務
- 嚮應用程序暴露代碼幾個方法,使應用程序能夠在運行時記錄日誌
- 基於日誌的等級過濾filter對象來決定要對那些日誌進行後續處理
- 將日誌信息傳給全部感興趣的日誌handlers
logger對象最經常使用的方法分爲兩類:
- 配置方法
- 消息發送方法
最經常使用的配置方法以下:
方法 | 描述 |
---|---|
Logger.setLevel() | 設置日誌器將會處理的日誌消息的最低嚴重級別 |
Logger.addHandler() 和 Logger.removeHandler() | 爲該logger對象添加 和 移除一個handler對象 |
Logger.addFilter() 和 Logger.removeFilter() | 爲該logger對象添加 和 移除一個filter對象 |
關於Logger.setLevel()方法的說明:
內建等級中,級別最低的是DEBUG,級別最高的是CRITICAL。例如setLevel(logging.INFO),此時函數參數爲INFO,那麼該logger將只會處理INFO、WARNING、ERROR和CRITICAL級別的日誌,而DEBUG級別的消息將會被忽略/丟棄。
logger對象配置完成後,可使用下面的方法來建立日誌記
方法 | 描述 |
---|---|
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() | 建立一個與它們的方法名對應等級的日誌記錄 |
Logger.exception() | 建立一個相似於Logger.error()的日誌消息 |
Logger.log() | 須要獲取一個明確的日誌level參數來建立一個日誌記錄 |
那麼,怎樣獲得一個Logger對象呢?一種方式是經過Logger類的實例化方法建立一個Logger類的實例,可是咱們一般都是用第二種方式--logging.getLogger()方法。
logging.getLogger()方法有一個可選參數name,該參數表示將要返回的日誌器的名稱標識,若是不提供該參數,則其值爲'root'。若以相同的name參數值屢次調用getLogger()方法,將會返回指向同一個logger對象的引用。
關於logger的層級結構與有效等級的說明:
- logger的名稱是一個以'.'分割的層級結構,每一個'.'後面的logger都是'.'前面的logger的children,例如,有一個名稱爲 foo 的logger,其它名稱分別爲 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的後代。
- logger有一個"有效等級(effective level)"的概念。若是一個logger上沒有被明確設置一個level,那麼該logger就是使用它parent的level;若是它的parent也沒有明確設置level則繼續向上查找parent的parent的有效level,依次類推,直到找到個一個明確設置了level的祖先爲止。須要說明的是,root logger老是會有一個明確的level設置(默認爲 WARNING)。當決定是否去處理一個已發生的事件時,logger的有效等級將會被用來決定是否將該事件傳遞給該logger的handlers進行處理。
- child loggers在完成對日誌消息的處理後,默認會將日誌消息傳遞給與它們的祖先loggers相關的handlers。所以,咱們沒必要爲一個應用程序中所使用的全部loggers定義和配置handlers,只須要爲一個頂層的logger配置handlers,而後按照須要建立child loggers就可足夠了。咱們也能夠經過將一個logger的propagate屬性設置爲False來關閉這種傳遞機制。
Handler類
Handler對象的做用是(基於日誌消息的level)將消息分發到handler指定的位置(文件、網絡、郵件等)。Logger對象能夠經過addHandler()方法爲本身添加0個或者更多個handler對象。好比,一個應用程序可能想要實現如下幾個日誌需求:
- 1)把全部日誌都發送到一個日誌文件中;
- 2)把全部嚴重級別大於等於error的日誌發送到stdout(標準輸出);
- 3)把全部嚴重級別爲critical的日誌發送到一個email郵件地址。
這種場景就須要3個不一樣的handlers,每一個handler複雜發送一個特定嚴重級別的日誌到一個特定的位置。
一個handler中只有很是少數的方法是須要應用開發人員去關心的。對於使用內建handler對象的應用開發人員來講,彷佛惟一相關的handler方法就是下面這幾個配置方法:
方法 | 描述 |
---|---|
Handler.setLevel() | 設置handler將會處理的日誌消息的最低嚴重級別 |
Handler.setFormatter() | 爲handler設置一個格式器對象 |
Handler.addFilter() 和 Handler.removeFilter() | 爲handler添加 和 刪除一個過濾器對象 |
須要說明的是,應用程序代碼不該該直接實例化和使用Handler實例。由於Handler是一個基類,它只定義了素有handlers都應該有的接口,同時提供了一些子類能夠直接使用或覆蓋的默認行爲。下面是一些經常使用的Handler:
Handler | 描述 |
---|---|
logging.StreamHandler | 將日誌消息發送到輸出到Stream,如std.out, std.err或任何file-like對象。 |
logging.FileHandler | 將日誌消息發送到磁盤文件,默認狀況下文件大小會無限增加 |
logging.handlers.RotatingFileHandler | 將日誌消息發送到磁盤文件,並支持日誌文件按大小切割 |
logging.hanlders.TimedRotatingFileHandler | 將日誌消息發送到磁盤文件,並支持日誌文件按時間切割 |
logging.handlers.HTTPHandler | 將日誌消息以GET或POST的方式發送給一個HTTP服務器 |
logging.handlers.SMTPHandler | 將日誌消息發送給一個指定的email地址 |
logging.NullHandler | 該Handler實例會忽略error messages,一般被想使用logging的library開發者使用來避免'No handlers could be found for logger XXX'信息的出現。 |
Formater類
Formater對象用於配置日誌信息的最終順序、結構和內容。與logging.Handler基類不一樣的是,應用代碼能夠直接實例化Formatter類。另外,若是你的應用程序須要一些特殊的處理行爲,也能夠實現一個Formatter的子類來完成。
Formatter類的構造方法定義以下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
可見,該構造方法接收3個可選參數:
- fmt:指定消息格式化字符串,若是不指定該參數則默認使用message的原始值
- datefmt:指定日期格式字符串,若是不指定該參數則默認使用"%Y-%m-%d %H:%M:%S"
- style:Python 3.2新增的參數,可取值爲 '%', '{'和 '$',若是不指定該參數則默認使用'%'
Filter類
Filter能夠被Handler和Logger用來作比level更細粒度的、更復雜的過濾功能。Filter是一個過濾器基類,它只容許某個logger層級下的日誌事件經過過濾。該類定義以下:
class logging.Filter(name='') filter(record)
好比,一個filter實例化時傳遞的name參數值爲'A.B',那麼該filter實例將只容許名稱爲相似以下規則的loggers產生的日誌記錄經過過濾:'A.B','A.B,C','A.B.C.D','A.B.D',而名稱爲'A.BB', 'B.A.B'的loggers產生的日誌則會被過濾掉。若是name的值爲空字符串,則容許全部的日誌事件經過過濾。
filter方法用於具體控制傳遞的record記錄是否能經過過濾,若是該方法返回值爲0表示不能經過過濾,返回值爲非0表示能夠經過過濾。
說明:
- 若是有須要,也能夠在filter(record)方法內部改變該record,好比添加、刪除或修改一些屬性。
- 咱們還能夠經過filter作一些統計工做,好比能夠計算下被一個特殊的logger或handler所處理的record數量等。
logging日誌流處理流程
咱們來描述下上面這個圖的日誌流處理流程:
- 1)(在用戶代碼中進行)日誌記錄函數調用,如:logger.info(...),logger.debug(...)等;
- 2)判斷要記錄的日誌級別是否知足日誌器設置的級別要求(要記錄的日誌級別要大於或等於日誌器設置的級別纔算知足要求),若是不知足則該日誌記錄會被丟棄並終止後續的操做,若是知足則繼續下一步操做;
- 3)根據日誌記錄函數調用時摻入的參數,建立一個日誌記錄(LogRecord類)對象;
- 4)判斷日誌記錄器上設置的過濾器是否拒絕這條日誌記錄,若是日誌記錄器上的某個過濾器拒絕,則該日誌記錄會被丟棄並終止後續的操做,若是日誌記錄器上設置的過濾器不拒絕這條日誌記錄或者日誌記錄器上沒有設置過濾器則繼續下一步操做--將日誌記錄分別交給該日誌器上添加的各個處理器;
- 5)判斷要記錄的日誌級別是否知足處理器設置的級別要求(要記錄的日誌級別要大於或等於該處理器設置的日誌級別纔算知足要求),若是不知足記錄將會被該處理器丟棄並終止後續的操做,若是知足則繼續下一步操做;
- 6)判斷該處理器上設置的過濾器是否拒絕這條日誌記錄,若是該處理器上的某個過濾器拒絕,則該日誌記錄會被當前處理器丟棄並終止後續的操做,若是當前處理器上設置的過濾器不拒絕這條日誌記錄或當前處理器上沒有設置過濾器測繼續下一步操做;
- 7)若是能到這一步,說明這條日誌記錄通過了層層關卡容許被輸出了,此時當前處理器會根據自身被設置的格式器(若是沒有設置則使用默認格式)將這條日誌記錄進行格式化,最後將格式化後的結果輸出到指定位置(文件、網絡、類文件的Stream等);
- 8)若是日誌器被設置了多個處理器的話,上面的第5-8步會執行屢次;
- 9)這裏纔是完整流程的最後一步:判斷該日誌器輸出的日誌消息是否須要傳遞給上一級logger(以前提到過,日誌器是有層級關係的)的處理器,若是propagate屬性值爲1則表示日誌消息將會被輸出處處理器指定的位置,同時還會被傳遞給parent日誌器的handlers進行處理直到當前日誌器的propagate屬性爲0中止,若是propagate值爲0則表示不向parent日誌器的handlers傳遞該消息,到此結束。
可見,一條日誌信息要想被最終輸出須要依次通過如下幾回過濾:
- 日誌器等級過濾;
- 日誌器的過濾器過濾;
- 日誌器的處理器等級過濾;
- 日誌器的處理器的過濾器過濾;
須要說明的是: 關於上面第9個步驟,若是propagate值爲1,那麼日誌消息會直接傳遞交給上一級logger的handlers進行處理,此時上一級logger的日誌等級並不會對該日誌消息進行等級過濾。
五、使用logging四大組件記錄日誌如今
咱們對logging模塊的重要組件及整個日誌流處理流程都應該有了一個比較全面的瞭解,下面咱們來看一個例子。
1. 需求
如今有如下幾個日誌記錄的需求:
- 1)要求將全部級別的全部日誌都寫入磁盤文件中
- 2)all.log文件中記錄全部的日誌信息,日誌格式爲:日期和時間 - 日誌級別 - 日誌信息
- 3)error.log文件中單獨記錄error及以上級別的日誌信息,日誌格式爲:日期和時間 - 日誌級別 - 文件名[:行號] - 日誌信息
- 4)要求all.log在天天凌晨進行日誌切割
2. 分析
- 1)要記錄全部級別的日誌,所以日誌器的有效level須要設置爲最低級別--DEBUG;
- 2)日誌須要被髮送到兩個不一樣的目的地,所以須要爲日誌器設置兩個handler;另外,兩個目的地都是磁盤文件,所以這兩個handler都是與FileHandler相關的;
- 3)all.log要求按照時間進行日誌切割,所以他須要用logging.handlers.TimedRotatingFileHandler; 而error.log沒有要求日誌切割,所以可使用FileHandler;
- 4)兩個日誌文件的格式不一樣,所以須要對這兩個handler分別設置格式器;
3. 代碼實現
import logging import logging.handlers import datetime print(datetime.datetime.now()) logger = logging.getLogger('mylogger') logger.setLevel(logging.DEBUG) # 設置郵箱存入本地加入時間間隔 rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.datetime.now()) rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) # 設置郵箱存入本地文件 f_handler = logging.FileHandler('error.log') f_handler.setLevel(logging.ERROR) f_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s")) # 設置日誌發送郵箱郵箱 ret = logging.handlers.SMTPHandler( mailhost=("smtp.163.com",25), # 設置服務器 fromaddr="chunsheng199433@163.com", # 設置用戶客戶端 toaddrs=["jiangyi@oldboyedu.com"],# 設置服務端 subject="logging from my app", # 設置標題 credentials=["chunsheng199433@163.com","*******"], #設置你的用戶名和密碼 ) ret.setLevel(logging.DEBUG) ret.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) # ret.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s")) logger.addHandler(rf_handler) logger.addHandler(ret) logger.addHandler(f_handler) logger.debug('debug message') logger.info('info message') logger.warning('warning message') logger.error('error message') logger.critical("critical 我是警告")
六、django使用日誌
settings.py
BASE_LOG_DIR = os.path.join(BASE_DIR, "logs") import logging LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '[%(levelname)s][%(asctime)s][%(filename)s][%(funcName)s][%(lineno)d] > %(message)s' }, 'simple': { 'format': '[%(levelname)s]> %(message)s', 'datefmt': '%Y-%m-%d %H:%M:%S' }, }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'file_handler': { 'level': 'INFO', 'class': 'logging.handlers.TimedRotatingFileHandler', 'filename': os.path.join(BASE_LOG_DIR, 'django.log'), 'formatter': 'standard', 'encoding': 'utf-8' }, # 用於文件輸出 'error': { # error 定義類型錯誤 'level': 'ERROR', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切 'filename': os.path.join(BASE_LOG_DIR, 'django.errorlog'), # 日誌文件 'maxBytes': 1024 * 1024 * 50, # 日誌大小 50M 'backupCount': 5, 'formatter': 'standard', 'encoding': 'utf-8', }, 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'formatter': 'standard' }, }, 'loggers': { 'mdjango': { # 一個記錄器中可使用多個處理器 'handlers': ['console', 'file_handler',"error"], # 掛載 'level': 'DEBUG', 'propagate': True, }, 'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': False, }, } } logger = logging.getLogger("mdjango")
views.py
from django.shortcuts import render,HttpResponse import logging from django_log.settings import logger # Create your views here. def login(request): logger.info("IndexHandler request Handler begin") logger.debug('query total:') logger.error("error") logger.warning("warning") return HttpResponse("ok")
本身定義
import logging class Singleton(object): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance app_name = "appName" log_file = "test.log" class SingletonLogger(Singleton): def __init__(self): super(SingletonLogger, self).__init__() self.logger = logging.getLogger(app_name) format_str = "[%(asctime)s][%(levelname)s]> %(message)s" formatter = logging.Formatter(format_str) file_handler = logging.FileHandler(log_file) file_handler.setFormatter(formatter) self.logger.addHandler(file_handler) self.logger.setLevel(logging.INFO) def debug(self, data): self.logger.debug(data) def info(self, data): self.logger.info(data) def warning(self, data): self.logger.warning(data) def error(self, data): self.logger.error(data) #執行函數 def test_log(): logger = SingletonLogger() #output the log msg logger.debug("this is the debug message") logger.info("this is the info message") logger.warning("this is the warning message") logger.error("this is the error message")