1、Logging
用來做簡單的日志。等級分為 debug()、 info()、 warning()、 error() 和 critical()
等級 | 使用場景 |
---|---|
DEBUG |
調試 ,打印詳細信息 |
INFO |
一般信息,打印關鍵信息,證明程序按預定軌跡執行。 |
WARNING |
警告信息,未預料到的 及可能出現問題和錯誤的提示信息,但是軟件還是會照常運行 例如:磁盤空間不足。 |
ERROR |
程序出現錯誤,可能會波及一些功能的使用 |
CRITICAL |
嚴重錯誤,軟件不能正常執行 |
logging組件:
Logger 記錄器: 記錄運行日志,並按配置日志等級過濾日志信息。

常用的方法分為兩類:配置和發送消息。
Logger.setLevel()
指定logger將會處理的日志等級信息。
Logger.addHandler()和Logger.removeHandler()
從記錄器對象中添加和刪除處理程序對象。
Logger.addFilter()和Logger.removeFilter()
從記錄器對象添加和刪除過濾器對象。
Handler 處理器: 將日志存放到控制台或寫入文件。

logging.StreamHandler
控制台輸出
logging.FileHandler
文件輸出
logging.handlers.RotatingFileHandler
按照大小自動分割日志文件,一旦達到指定的大小重新生成文件
logging.handlers.TimedRotatingFileHandler
按照時間自動分割日志文件
配置方法:
setLevel()方法
指明了將會分發日志的最低級別。為什么會有兩個setLevel()方法?記錄器的級別決定了消息是否要傳遞給處理器。
每個處理器的級別決定了消息是否要分發。
setFormatter()
為該處理器選擇一個格式化器。
addFilter()和removeFilter()
分別配置和取消配置處理程序上的過濾器對象。
Filter 過濾器: 過濾輸出的日志信息。

addFilter(filter),removeFilter(filter)和filter(record)方法
常用的屬性
name 就是初始化logger對象時傳入的名字
level 是級別
pathname 是哪個文件輸出的這行日志
lineno 是行號
msg 是日志本身
Formatter 格式化器: 格式化日志輸出格式。

默認的時間格式為%Y-%m-%d %H:%M:%S 常用信息 %(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 用戶輸出的消息
一、引用包
import logging from logging import handles
二、配置日志輸出格式
logging.basicConfig函數各參數(紅色為常用信息)
filename: 指定日志文件名,將文件寫入 filemode: 和file函數意義相同,指定日志文件的打開模式,'w' 覆蓋寫入 或'a' 追加寫入 ,默認寫入模式為a format: 指定輸出的格式和內容,format可以輸出很多有用信息,如上例所示: %(levelno)s: 打印日志級別的數值 %(levelname)s: 打印日志級別 %(pathname)s: 打印當前執行程序名(包含詳細路徑),其實就是sys.argv[0] %(filename)s: 打印當前執行程序名 %(funcName)s: 打印日志的當前函數 %(lineno)d: 打印日志的當前行號 %(asctime)s: 打印日志的時間 %(thread)d: 打印線程ID %(threadName)s: 打印線程名稱 %(process)d: 打印進程ID %(message)s: 打印日志信息 datefmt: 指定時間格式,同time.strftime()
英文: %A 周, %B 月
英文縮寫: %a 周, %b 月, %c 日期+時間縮寫 (輸出格式:Wed Jan 12 10:00:06 2022)
時間: %d 日, %w 周, %m 月, %Y 年, %H:%M:%S 時分秒, %T 時間 (10:09:32), %D 日期 (12/01/22), %F 日期(2022-01-12)
level: 設置日志級別,默認為logging.WARNING
stream: 指定將日志的輸出流,可以指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,當stream和filename同時指定時,stream被忽略
設置日志格式
# 設置打印格式 logging.basicConfig(format='%(asctime)s - %(name)s[line:%(lineno)d] - %(levelname)s: %(message)s', # 日志格式 datefmt='%F %T ', # 日期格式 level=logging.INFO) # 日志等級info
運行結果:
三、打印日志(單一打印日志)
1、僅打印到控制台
# coding:utf-8 import logging # 打印到控制台 logging.debug("打印到控制台") logging.info("打印到控制台") logging.warning("打印到控制台") logging.error("打印到控制台") logging.critical("打印到控制台")
logging 默認設置的等級為warning
運行結果:
2、僅將日志寫入文件
# 設置打印格式 logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s', # 日志格式 datefmt='%F %T ', # 日期格式 level=logging.INFO,# 日志等級 filename="info.log", # 日志寫入文件info.log filemode="a") #a 追加寫入, w 覆蓋寫入
運行結果:
info.log內容:
四、輸出控制台並寫入日志文件
#!/usr/bin/env python # -*- coding:utf-8 -*- import os, datetime, logging from logging import handlers # >>> 定義Logger類 class Logger(object): # 定義類屬性 level_relations = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, 'crit': logging.CRITICAL}#日志級別關系映射 # 使用函數exists()對文件存在與否進行判斷,存在為True,不存在為False. if os.path.exists('logs') == True: # 文件存在 filepath = os.getcwd() + '\logs\log_' + datetime.datetime.now().strftime('%Y%m%d') + '.log' else: os.makedirs(".\logs") # 創建文件夾 filepath = os.getcwd() + '\logs\log_' + datetime.datetime.now().strftime('%Y%m%d') + '.log' # 定義構造函數 when=D 天數,新生成的文件名上會帶上時間 backCount 保留文件生成個數 def __init__(self, filename=filepath, level='info', when='D', backCount=3, fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'): #when:描述滾動周期的基本單位;“S”: Seconds ,“M”: Minutes ,“H”: Hours ,“D”: Days ,“W”: Week day (0=Monday) ,“midnight”: Roll over at midnight self.logger = logging.getLogger(filename) # 指定日志文件名 format_str = logging.Formatter(fmt) # 設置日志格式 self.logger.setLevel(self.level_relations.get(level)) # 設置日志級別 sh = logging.StreamHandler() # 往屏幕上輸出 sh.setFormatter(format_str) # 設置屏幕上顯示的格式 th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount, encoding='utf-8') # 寫入文件 th.setFormatter(format_str) # 設置文件里寫入的格式 self.logger.addHandler(sh) # 把對象加到logger里 屏幕輸出 self.logger.addHandler(th) # 把對象加到logger里 文件寫入 def getLog(self): return self.logger #創建log對象,隱式調用了我們手動創建的 __init__() 構造方法,並設置日志等級 # logs = Logger().logger # 方法一 # logs = Logger().getLog() # 方法二 # 方法三:重置日志等級 logs = Logger(level="debug").getLog() # 該部分只有文件作為腳本時才會被執行,而 import 到其他腳本中是不會被執行的 if __name__ == '__main__': logs.debug('debug 信息') logs.info('info 信息') logs.warning('警告 信息') logs.error('報錯 信息') logs.critical('嚴重 信息')
運行結果
控制台:
.log文件:
自動分割日志文件

import logging from logging import handlers class Logger(object): level_relations = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, 'crit': logging.CRITICAL}#日志級別關系映射 def __init__(self, filename, level='info', when='D', backCount=3, fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'): self.logger = logging.getLogger(filename) # 創建一個logger format_str = logging.Formatter(fmt)#設置日志格式 self.logger.setLevel(self.level_relations.get(level))#設置日志級別 sh = logging.StreamHandler()#往屏幕上輸出 sh.setFormatter(format_str) #設置屏幕上顯示的格式 th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount, encoding='utf-8') #往文件里寫入#指定間隔時間自動生成文件的處理器 th.setFormatter(format_str)#設置文件里寫入的格式 self.logger.addHandler(sh) #把對象加到logger里 self.logger.addHandler(th)# if __name__ == '__main__': log = Logger('./logs/debug.log', level='debug') log.logger.debug('debug') log.logger.info('info') log.logger.warning('警告') log.logger.error('報錯') log.logger.critical('嚴重') Logger('./logs/error.log', level='error').logger.error('error')
運行結果:
2、loguru
# 安裝 pip install loguru
# 引用 from loguru import logger as logg
# 打印日志 logg.info("debug")
# 寫入文件 # logg.add("logs_20220414.log") # 將日志保存在當前路徑上一級目錄的logs目錄下,命名為 log_name log_name = "log_{}.log".format(datetime.datetime.now().strftime('%Y%m%d')) logs.add(sink=os.path.abspath("..") + "\\logs\\{}".format(log_name), level=log_level.upper()) # 設置生成日志文件,utf-8編碼,每天0點切割,zip壓縮,保留3天,異步寫入 # logs.add(sink='test.log', level="INFO", rotation="00:00", retention="3 days", compression="zip", encoding="utf-8", enqueue=True)

from loguru import logger as logg logg.add("logs.log") def case(): logg.debug("debug") logg.warning("warning") case()
執行結果:
# 僅輸出到文件 不打印到控制台 # 刪除以前添加的處理程序並停止向其接收器發送日志。 logs.remove(handler_id=None) # 移除控制台輸出
# 集成loguru到控制台(即html報告) class PropogateHandler(logging.Handler): def emit(self, record): logging.getLogger(record.name).handle(record) logs.add(PropogateHandler(), format="{time:YYYY-MM-DD at HH:mm:ss} | {message}")
執行結果: