## logging模塊 #### 什麼是logging模塊 logging模塊是python提供的用於記錄日誌的模塊 #### 爲何須要logging 咱們徹底能夠本身打開文件而後,日誌寫進去,可是這些操做重複且沒有任何技術含量,因此python幫咱們進行了封裝,有了logging後咱們在記錄日誌時 只須要簡單的調用接口便可,很是方便! #### 日誌級別 在開始記錄日誌前還須要明確,日誌的級別 隨着時間的推移,日誌記錄會很是多,成千上萬行,如何快速找到須要的日誌記錄這就成了問題 解決的方案就是 給日誌劃分級別 logging模塊將日誌分爲了五個級別,從高到低分別是: 1.info 常規信息 2.debug 調試信息 3.warning 警告信息 4.error 錯誤信息 5.cretical 嚴重錯誤 本質上他們使用數字來表示級別的,從高到低分別是10,20,30,40,50 ## logging模塊的使用 ```python #1.導入模塊 import logging #2.輸出日誌 logging.info("info") logging.debug("debug") logging.warning("warning") logging.error("error") logging.critical("critical") #輸出 WARNING:root:warning #輸出 ERROR:root:error #輸出 CRITICAL:root:critical ``` 咱們發現info 和 debug都沒有輸出,這是由於它們的級別不夠, 默認狀況下: ``` logging的最低顯示級別爲warning,對應的數值爲30 日誌被打印到了控制檯 日誌輸出格式爲:級別 日誌生成器名稱 日誌消息 ``` 如何修改這寫默認的行爲呢?,這就須要咱們本身來進行配置 ## 自定義配置 ```python import logging logging.basicConfig() """可用參數 filename:用指定的文件名建立FiledHandler(後邊會具體講解handler的概念),這樣日誌會被存儲在指定的文件中。 filemode:文件打開方式,在指定了filename時使用這個參數,默認值爲「a」還可指定爲「w」。 format:指定handler使用的日誌顯示格式。 datefmt:指定日期時間格式。 level:設置rootlogger(後邊會講解具體概念)的日誌級別 """ #案例: logging.basicConfig( filename="aaa.log", filemode="at", datefmt="%Y-%m-%d %H:%M:%S %p", format="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s", level=10 ) ``` #### 格式化所有可用名稱 ```python %(name)s:Logger的名字,並不是用戶名,詳細查看 %(levelno)s:數字形式的日誌級別 %(levelname)s:文本形式的日誌級別 %(pathname)s:調用日誌輸出函數的模塊的完整路徑名,可能沒有 %(filename)s:調用日誌輸出函數的模塊的文件名 %(module)s:調用日誌輸出函數的模塊名 %(funcName)s:調用日誌輸出函數的函數名 %(lineno)d:調用日誌輸出函數的語句所在的代碼行 %(created)f:當前時間,用UNIX標準的表示時間的浮 點數表示 %(relativeCreated)d:輸出日誌信息時的,自Logger建立以 來的毫秒數 %(asctime)s:字符串形式的當前時間。默認格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒 %(thread)d:線程ID。可能沒有 %(threadName)s:線程名。可能沒有 %(process)d:進程ID。可能沒有 %(message)s:用戶輸出的消息 ``` 至此咱們已經能夠本身來配置一 寫基礎信息了,可是當咱們想要將同一個日誌輸出到不一樣位置時,這些基礎配置就沒法實現了, 例如 有一個登陸註冊的功能 須要記錄日誌,同時生成兩份 一份給程序員看,一份給老闆看,做爲程序員應該查看較爲詳細的日誌,二老闆則應該簡單一些,由於他不須要關心程序的細節 要實現這樣的須要咱們須要系統的瞭解loggin模塊 #### logging模塊的四個核心角色 1.Logger 日誌生成器 產生日誌 2.Filter 日誌過濾器 過濾日誌 3.Handler 日誌處理器 對日誌進行格式化,並輸出到指定位置(控制檯或文件) 4.Formater 處理日誌的格式 #### 一條日誌完整的生命週期 1.由logger 產生日誌 -> 2.交給過濾器判斷是否被過濾 -> 3.將日誌消息分發給綁定的全部處理器 -> 4處理器按照綁定的格式化對象輸出日誌 其中 第一步 會先檢查日誌級別 若是低於設置的級別則不執行 第二步 使用場景很少 須要使用面嚮對象的技術點 後續用到再講 第三步 也會檢查日誌級別,若是獲得的日誌低於自身的日誌級別則不輸出 ``` 生成器的級別應低於句柄不然給句柄設置級別是沒有意義的, 例如 handler設置爲20 生成器設置爲30 30如下的日誌壓根不會產生 ``` 第四步 若是不指定格式則按照默認格式 #### logging各角色的使用(瞭解) ```python # 生成器 logger1 = logging.getLogger("日誌對象1") # 文件句柄 handler1 = logging.FileHandler("log1.log",encoding="utf-8") handler2 = logging.FileHandler("log2.log",encoding="utf-8") # 控制檯句柄 handler3 = logging.StreamHandler() # 格式化對象 fmt1 = logging.Formatter( fmt="%(asctime)s - %(name)s - %(levelname)s: %(message)s", datefmt="%m-%d %H:%M:%S %p") fmt2 = logging.Formatter( fmt="%(asctime)s - %(levelname)s : %(message)s", datefmt="%Y/%m/%d %H:%M:%S") # 綁定格式化對象與文件句柄 handler1.setFormatter(fmt1) handler2.setFormatter(fmt2) handler3.setFormatter(fmt1) # 綁定生成器與文件句柄 logger1.addHandler(handler1) logger1.addHandler(handler2) logger1.addHandler(handler3) # 設置日誌級別 logger1.setLevel(10) #生成器日誌級別 handler1.setLevel(20) #句柄日誌級別 # 測試 logger1.debug("debug msessage") logger1.info("info msessage") logger1.warning("warning msessage") logger1.critical("critical msessage") ``` 到此咱們已經能夠實現上述的需求了,可是這並非咱們最終的實現方式,由於每次都要編寫這樣的代碼是很是痛苦的 #### logging的繼承(瞭解) 能夠將一個日誌指定爲另外一個日誌的子日誌 或子孫日誌 當存在繼承關係時 子孫級日誌收到日誌時會將該日誌向上傳遞 指定繼承關係: ```python import logging log1 = logging.getLogger("mother") log2 = logging.getLogger("mother.son") log3 = logging.getLogger("mother.son.grandson") # handler fh = logging.FileHandler(filename="cc.log",encoding="utf-8") # formatter fm = logging.Formatter("%(asctime)s - %(name)s -%(filename)s - %(message)s") # 綁定 log1.addHandler(fh) log2.addHandler(fh) log3.addHandler(fh) # 綁定格式 fh.setFormatter(fm) # 測試 # log1.error("測試") # log2.error("測試") log3.error("測試") # 取消傳遞 log3.propagate = False # 再次測試 log3.error("測試") ``` #### 經過字典配置日誌模塊(重點) 每次都要編寫代碼來配置很是麻煩 ,咱們能夠寫一個完整的配置保存起來,以便後續直接使用 ```python import logging.config logging.config.dictConfig(LOGGING_DIC) logging.getLogger("aa").debug("測試") ``` **LOGGING_DIC模板** ```python standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' logfile_path = "配置文件路徑" LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, 'handlers': { #打印到終端的日誌 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, #打印到文件的日誌,收集info及以上的日誌 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path, # 日誌文件 'maxBytes': 1024*1024*5, # 日誌大小 5M 'backupCount': 5, #日誌文件最大個數 'encoding': 'utf-8', # 日誌文件的編碼 }, }, 'loggers': { #logging.getLogger(__name__)拿到的logger配置 'aa': { 'handlers': ['default', 'console'], # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)傳遞 }, }, } ``` 補充: getLogger參數就是對應字典中loggers的key , 若是沒有匹配的key 則返回系統默認的生成器,咱們能夠在字典中經過空的key來將一個生成器設置爲默認的 ```python 'loggers': { # 把key設置爲空 '': { 'handlers': ['default', 'console'], # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)傳遞 }, }, ``` ,日後在使用時能夠這調用模塊提供的函數,來輸出日誌 logging.info("測試信息!") 另外咱們在第一次使用日誌時並無指定生成器,但也能夠使用,這是由於系統有默認的生成器名稱就叫root 最後來完成以前的需求: 有一個登陸註冊的功能 須要記錄日誌,同時生成兩份 一份給程序員看,一份給老闆看,做爲程序員應該查看較爲詳細的日誌,二老闆則應該簡單一些,由於他不須要關心程序的細節 ```python # 程序員看的格式 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字 logfile_path1 = "coder.log" # 老闆看的格式 simple_format = '[%(levelname)s][%(asctime)s]%(message)s' logfile_path2 = "boss.log" LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, 'handlers': { #打印到終端的日誌 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, #打印到文件的日誌,收集info及以上的日誌 'std': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path1, # 日誌文件 'maxBytes': 1024*1024*5, # 日誌大小 5M 'backupCount': 5, #日誌文件最大個數 'encoding': 'utf-8', # 日誌文件的編碼 }, 'boss': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'simple', 'filename': logfile_path2, # 日誌文件 'maxBytes': 1024 * 1024 * 5, # 日誌大小 5M 'backupCount': 5, # 日誌文件最大個數 'encoding': 'utf-8', # 日誌文件的編碼 } }, 'loggers': { #logging.getLogger(__name__)拿到的logger配置 'aa': { 'handlers': ['std', 'console',"boss"], # 這裏把上面定義的handler都加上,即log數據會同時輸出到三個位置 'level': 'INFO', 'propagate': True, # 向上(更高level的logger)傳遞 }, }, } ```
,日後在使用時能夠這調用模塊提供的函數,來輸出日誌 logging.info("測試信息!") 另外咱們在第一次使用日誌時並無指定生成器,但也能夠使用,這是由於系統有默認的生成器名稱就叫root 最後來完成以前的需求: 有一個登陸註冊的功能 須要記錄日誌,同時生成兩份 一份給程序員看,一份給老闆看,做爲程序員應該查看較爲詳細的日誌,二老闆則應該簡單一些,由於他不須要關心程序的細節 ```python # 程序員看的格式 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字 logfile_path1 = "coder.log" # 老闆看的格式 simple_format = '[%(levelname)s][%(asctime)s]%(message)s' logfile_path2 = "boss.log" LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, 'handlers': { #打印到終端的日誌 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, #打印到文件的日誌,收集info及以上的日誌 'std': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path1, # 日誌文件 'maxBytes': 1024*1024*5, # 日誌大小 5M 'backupCount': 5, #日誌文件最大個數 'encoding': 'utf-8', # 日誌文件的編碼 }, 'boss': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'simple', 'filename': logfile_path2, # 日誌文件 'maxBytes': 1024 * 1024 * 5, # 日誌大小 5M 'backupCount': 5, # 日誌文件最大個數 'encoding': 'utf-8', # 日誌文件的編碼 } }, 'loggers': { #logging.getLogger(__name__)拿到的logger配置 'aa': { 'handlers': ['std', 'console',"boss"], # 這裏把上面定義的handler都加上,即log數據會同時輸出到三個位置 'level': 'INFO', 'propagate': True, # 向上(更高level的logger)傳遞 }, }, } ```
---恢復內容結束---python