【Python】Logging 模块 _ 打印及保存日志
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}")
执行结果:
-------------------------------------------------------------------------------------
如果万事开头难 那请结局一定圆满 @ Phoenixy
-------------------------------------------------------------------------------------