python日志模块,可以说在工程项目中应该很广泛,本文简单地介绍Logging封装模块的编写及使用。当前环境:python 3.6.8;编译环境:pycharm。
目录
1、logging简单配置
我们来看一下最为简朴的日志配置及输出👇
# -*- coding:utf-8 -*-
import logging
#最为简单的配置输出方式与日志级别
logging.basicConfig(filename='logger.log', level="INFO")
#输出不同级别的日志
logging.debug('debug message')
logging.info('info message')
logging.warnning('warn message')
logging.error('error message')
logging.critical('critical message')
输出至同目录下"logger.log 文件",内容如下👇
可以看到,配置语句中的 filename 和 level 各自起到了作用,前者作用是给输出的 .log 文件进行命名,后者则是设置输出内容的等级设置,比如设置日志级别为 INFO ,那么只有日志级别大于等于 INFO 的日志才会输出,可以看出logger.log 文件中 DEBUG 等级内容没有输出,因为其等级是低于 INFO 等级的。
需要注意的是,标准输出(屏幕/控制台)未显示任何信息,因为上边语句只涉及日志文件的输出,关于屏幕输出在后文详述。
2、logging原理
2.1、日志事件级别
级别 | 日志使用 |
DEBUG | 细节信息,仅当诊断问题时适用。 |
INFO | 确认程序按预期运行 |
WARNING | 表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行 |
ERROR | 由于严重的问题,程序的某些功能已经不能正常执行 |
CRITICAL | 严重的错误,表明程序已不能继续执行 |
从 logging 简单配置可以得出,日志事件的级别是控制输出内容(日志文件或屏幕)的阀门,默认的级别是 WARNING ,意味着只会追踪该级别及以上的事件,一般情况下我们都会依据项目的情况进行更改。级别排序:DEBUG < INFO < WARNING < ERROR < CRITICAL ,个人认为该排序可能取决于各自级别的严重程度,级别错误越严重,那么它级别越大。
2.2、日志组件
日志文件的封装流程大致如下图所示👇
①Loggers记录器
提供应用程序的调用接口,其他py项目或文件可以调用该接口编写日志。Logger 也是一个树形层级结构,在设置等级之前必须创建 Logger 实例,即创建一个记录器,如果没有显式的进行创建,则默认创建一个 root logger ,相关设置均默认。
logger = logging.getLogger(logger_name)
#创建一个logger
创建 Logger 实例后,可以使用以下方法进行日志级别设置,增加处理器 Handler 。👇
logger.setLevel("ERROR") # 设置日志级别为ERROR,即只有日志级别大于等于ERROR的日志才会输出
logger.addHandler(handler_name) # 为Logger实例增加一个处理器
logger.removeHandler(handler_name) # 为Logger实例删除一个处理器
②Handler 处理器
Handler 处理器类型有很多种,本文介绍两个— StreamHandler 和 FileHandler,
创建 Handler 之后,可以通过使用以下方法设置日志级别,设置格式化器 Formatter 。
xx = logging.xxHandler() #xx为Stream或File
xx.setLevel(logging.WARN) # 指定日志级别,低于WARN级别的日志将被忽略
xx.setFormatter(formatter_name) # 设置一个格式化器formatter
③Formatter 格式化器
使用 Formatter 对象设置日志信息最后的规则、结构和内容,默认的时间格式为:%Y-%m-%d %H:%M:%S。
formatter = logging.Formatter(fmt=None, datefmt=None)
其中,fmt 是消息的格式化字符串,datefmt 是日期字符串。如果不指明 fmt ,将使用'%(message)s'。如果不指明 datefmt ,将使用ISO8601日期格式。
注:Logger可以包含一个或多个Handler,即Logger与Handler是一对多的关系;一个Logger实例可以新增多个Handler,一个Handler可以新增多个格式化器,而且日志级别将会继承。
3、logging日志封装
本文采用 Logger.py 文件显示创建记录器Logger、处理器Handler和格式化器Formatter,并进行相关设置,Logger.py 文件👇
import logging
import time
import os
class Logger(object):
def __init__(self,name=None):
self.name = name
#①创建一个记录器
self.logger = logging.getLogger(self.name)
self.logger.setLevel("INFO") # 设置日志级别为 'level',即只有日志级别大于等于'level'的日志才会输出
self.formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") #创建formatter
#②创建屏幕-输出到控制台,设置输出等级
self.streamHandler = logging.StreamHandler()
self.streamHandler.setLevel("DEBUG")
#③创建log文件,设置输出等级
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 根目录
time_now = time.strftime('%Y_%m%d_%H', time.localtime()) + '.log' # log文件命名:2022_0402_21.log
self.fileHandler = logging.FileHandler(os.path.join(PROJECT_ROOT, "Log", time_now), 'a', encoding='utf-8')
self.fileHandler.setLevel("DEBUG")
#④用formatter渲染这两个Handler
self.streamHandler.setFormatter(self.formatter)
self.fileHandler.setFormatter(self.formatter)
#⑤将这两个Handler加入logger内
self.logger.addHandler(self.streamHandler)
self.logger.addHandler(self.fileHandler)
def getLogger(self):
return self.logger
具体操作在 Logger.py 文件内部有所讲解,简单的封装就是这样,至于更进一步的话可以考虑将方法里边的参数提取出来,放在常量文件或配置文件均可。
既然方法写完了,接下来试用一下~👇
if __name__ == "__main__":#检测日志打印效果
logger = Logger('baseLogger.py').getLogger() #调用logger接口
def div_calcul():
try:
result = 5 / 0
print(result)
logger.info("输出结果成功")
except Exception as e:
logger.error("[计算出错原因为:%s]" % e)
div_calcul() #执行该方法,预期会报错
屏幕输出:
log文件输出:
完成我们所要达到的目标!