python3 配置logging日誌類的操作
配置類config_file:
from configparser import ConfigParser class config_file: def __init__(self,conf_filePath,encoding="utf-8"): #開啟配置檔案,例項化ConfigParser類,並以預設utf-8的編碼格式讀取檔案 self.cf = ConfigParser() self.cf.read(conf_filePath,encoding) def get_Int_Value(self,section,option): #獲取整數 return self.cf.getint(section,option) def get_boolValue(self,option): #獲取布林值 return self.cf.getboolean(section,option) def get_strValue(self,option): # 獲取字串型別的值 return self.cf.get(section,option) def get_floatValue(self,option): # 獲取浮點數值 return self.cf.getfloat(section,option) def get_sections(self): # 獲取所有的section return self.cf.sections() def get_options(self,section): # 獲取所有的option return self.cf.options(section)
日誌類:
from configparser import ConfigParser import logging from config_file import config_file class Log_Test(config_file):#繼承config_file def logging(self): logger = logging.getLogger(self.get_strValue('log','logger_name')) #從配置檔案讀取logger名 logger.setLevel(self.get_strValue('log','logger_level')) # 設定logger收集器的收集log級別 format_logger = logging.Formatter(self.get_strValue('log','logger_format')) if(self.get_boolValue('log','logger_out')): handle = logging.StreamHandler() # 指定輸出到console控制檯 handle.setLevel(self.get_strValue('log','logger_level')) # 讀取日誌等級並設定logging的級別 handle.setFormatter(format_logger) # 指定日誌格式 else: handle = logging.FileHandler(self.get_strValue('log','logger_filepath'),encoding='utf-8') handle.setLevel(self.get_strValue('log','logger_level')) # 讀取日誌等級並設定logging的級別 handle.setFormatter(format_logger) # 指定日誌格式 logger.addHandler(handle) return logger
日誌配置檔案logging.cfg:
[log] #日誌收集器 logger_name=TEST #日誌級別 級別需要大寫 DEBUG-->INFO-->WARNING-->ERROR-->CRITICAL/FATAL logger_level=DEBUG #日誌輸出格式 注意轉義 logger_format=%%(asctime)s-%%(filename)s-%%(levelname)s-日誌資訊:%%(message)s #日誌是否輸出到控制檯 True or False logger_out=False #日誌輸出指定檔案地址 logger_filepath=logging_Test.log
將讀取配置檔案類進行封裝,日誌類繼承配置類。
補充知識:Python2/Python3自定義日誌類教程
一、說明
1.1 背景說明
Python的logging功能是比較豐富的支援不同層次的日誌輸出,但或是我們想在日誌前輸出時間、或是我們想要將日誌輸入到檔案,我們還是想要自定義日誌類。
之前自己也嘗試寫過但感覺文件太亂看不懂怎麼寫,今天有人拿個半成品來問為什麼程式碼報錯,在其基礎上改造了一下。
1.2 logging級別說明
logging日誌級別及對應值如下,預設情況下直接執行只有INFO及以上級別才會輸出(本質上是大於等於20才會輸出),除錯模式執行DEBUG日誌才會輸出。
可以通過自定義輸出日誌級別,指定直接執行輸出什麼級別的日誌;不過除錯模式列印的日誌應該是不可以修改的。
Level |
Numeric value |
---|---|
CRITICAL |
50 |
ERROR |
40 |
WARNING |
30 |
INFO |
20 |
DEBUG |
10 |
NOTSET |
0 |
二、實現程式碼
2.1 Python2實現程式碼
# -*- coding: utf-8 -*- import os import datetime import logging class LogConfig: def __init__(self,log_type="console"): # 指定日誌輸出到控制檯時的初始化 if log_type == "console": logging.basicConfig(level=logging.INFO,format='%(asctime)s %(levelname)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S',) # 指定日誌輸出到檔案的初始化 elif log_type == "file": # 建立存放日誌的目錄 if not os.path.exists('./log'): os.mkdir('./log') # 作業系統本身不允許檔名包含:等特殊字元,所以這裡也不要用,不然賦給filename時會報錯 nowTime = datetime.datetime.now().strftime('%Y-%m-%d') file_name = './log/%s.log' % nowTime # python2.7也有logging.basicConfig(),但只直接用logging.basicConfig(),寫中文時會報錯 # 所以為風格統一,我們這裡不使用logging.basicConfig(),全通過set設定 root_logger = logging.getLogger() root_logger.setLevel(logging.INFO) handler = logging.FileHandler(filename=file_name,encoding='utf-8',mode='a') formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') handler.setFormatter(formatter) root_logger.addHandler(handler) def getLogger(self): logger = logging.getLogger() return logger if __name__ == "__main__": # log_type = "console" log_type = "file" logger = LogConfig(log_type).getLogger() logger.debug('print by debug') logger.info('print by info') logger.warning('print by warning')
2.2 Python3實現程式碼
python3.3 之後logging.basicConfig()中提供了handlers引數,我們可藉助handlers引數來指定編碼。
python3.3之前的python3版本寫法得和python2一樣。另外python3.9之後logging.basicConfig()會直接提供encoding引數,到時可以更方便。
import os import datetime import logging class LogConfig: def __init__(self,) # 指定日誌輸出到檔案的初始化 elif log_type == "file": # 建立存放日誌的目錄 if not os.path.exists('./log'): os.mkdir('./log') # 作業系統本身不允許檔名包含:等特殊字元,所以這裡也不要用,不然賦給filename時會報錯 nowTime = datetime.datetime.now().strftime('%Y-%m-%d') file_name = './log/%s.log' % nowTime file_handler = logging.FileHandler(filename=file_name,mode='a') # level----指定列印的日誌等級;預設為WARNING;可為NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL # format----指定整條日誌的格式;這裡設定為“時間-等級-日誌內容” # datefmt----format中時間的格式; # filename----日誌輸出到的檔案;預設列印到控制檯 # filemode----日誌檔案讀寫形式;預設為“a”;配合filename使用,如果不用filename該引數也可不用 # 本來輸出到檔案使用filename和filemode兩個引數就可以了,不需要handlers # 但是logging將日誌輸出到檔案時中文會亂碼,而logging.basicConfig又沒有提供指定編碼的引數(python3.9之後才提供有直接的encoding引數) # 要指定編碼只能使用handlers。另外handlers還是python3.3 之後才提供的引數,在此之前的版本請參考python2的寫法 logging.basicConfig(level=logging.INFO,# filename=file_name,# filemode='a',handlers=[file_handler],) def getLogger(self): logger = logging.getLogger() return logger if __name__ == "__main__": # log_type = "console" log_type = "file" logger = LogConfig(log_type).getLogger() logger.debug('print by debug') logger.info('print by info') logger.warning('print by warning')
三、日誌截圖
四、更科學的日誌定義方式(20200310更新)
通過近段時間的使用發現原先的方法就自己用用沒問題,但與別人產生呼叫及上生產時就會存在幾個問題:
第一個問題是,直接通過logging.basicConfig()進行配置,會同時影響被呼叫庫的日誌設定。
第二個問題是,原現的檔案日誌形式只能輸出到一個給定的檔案,不能實現不同的日誌型別輸出到不同的日誌檔案。
第三個問題是,原現的檔案日誌形式使用"w"模式則上次日誌會被清除,使用"a"模式則日誌又會無限增長需要注意清理。
前兩個問題通過getLogger時給定一個名稱而不是直接獲取根logger進行處理;第三個問題通過使用TimedRotatingFileHandler等替換FileHandler進行處理。
更新程式碼如下:
import os import datetime import logging import logging.handlers class LogConfig: def __init__(self): pass def get_console_logger(self): def _gen_file_logger_handler(): _handler = logging.StreamHandler() formatter = logging.Formatter( "[%(asctime)s %(msecs)03d][%(process)d][tid=%(thread)d][%(name)s][%(levelname)s] %(message)s [%(filename)s" " %(funcName)s %(lineno)s] ",datefmt="%Y-%m-%d %H:%M:%S") _handler.setLevel(logging.INFO) _handler.setFormatter(formatter) return _handler def _gen_console_logger(): # 解決第一個問題--logging.basicConfig()會影響被呼叫庫的日誌--getLogger時給定一個名稱而不是直接獲取根logger _console_logger = logging.getLogger("console_logger") _console_logger.addHandler(handler) return _console_logger handler = _gen_file_logger_handler() console_logger = _gen_console_logger() return console_logger def get_file_logger(self,log_file_name): def _make_sure_log_dir_exist(): if not os.path.isdir(log_file_dir): os.mkdir(log_file_dir) def _gen_file_logger_handler(): # 作業系統本身不允許檔名包含:等特殊字元,所以這裡也不要用,不然賦給filename時會報錯 # nowTime = datetime.datetime.now().strftime('%Y-%m-%d') file_path = f'{log_file_dir}/{log_file_name}' formatter = logging.Formatter( "[%(asctime)s %(msecs)03d][%(process)d][tid=%(thread)d][%(name)s][%(levelname)s] %(message)s [%(filename)s" " %(funcName)s %(lineno)s] ",datefmt="%Y-%m-%d %H:%M:%S") # 解決第三個問題--日誌會不斷增大需要手動去清理--使用TimedRotatingFileHandler等替換FileHandler # filename----日誌檔案 # when----更換日誌檔案的時間單位 # interval----更換日誌檔案的時間單位個數;這裡是7天換一個檔案 # backupCount----儲存的舊日誌檔案個數;這裡即只保留上一個日誌檔案 # encoding----日誌檔案編碼 _handler = logging.handlers.TimedRotatingFileHandler(filename=file_path,when='D',interval=7,backupCount=1,encoding='utf-8') # 實際發現有些時候這裡setLevel並不起作用 # _handler.setLevel(logging.INFO) _handler.setFormatter(formatter) return _handler def _gen_file_logger(): # 解決第二個問題--不能定義多個日誌檔案--getLogger時給定一個名稱而不是直接獲取根logger _file_logger = logging.getLogger(log_file_name) _file_logger.addHandler(handler) return _file_logger log_file_dir = "log" _make_sure_log_dir_exist() handler = _gen_file_logger_handler() file_logger = _gen_file_logger() # 實際發現有些時候handler的setLevel並不起作用,要在這裡setLevel file_logger.setLevel(logging.INFO) return file_logger if __name__ == "__main__": # log_type = "console" # logger = LogConfig().get_console_logger() log_type = "file" # log_file_name不同,返回的是不同的logger,這樣就可以方便地定義多個logger log_file_name = "random_file_name.log" logger = LogConfig().get_file_logger(log_file_name=log_file_name) logger.debug('print by debug') logger.info('print by info') logger.warning('print by warning')
以上這篇python3 配置logging日誌類的操作就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。