日志在实际生产工作中有着很重要的作用,之前一直不太注意这方面,导致生产环境遇到问题无从下手,借着整理把日志这块的功能熟悉下。网上有很多相关的介绍,不过还是建议大家浏览下官方相关的资料。官方文档的布局都差不多这里以3.8.12的版本为例:logging — Logging facility for Python — Python 3.8.12 documentation
文档对logging模块的功能整体上做了个简单的介绍,左边的红框说logging模块是线程安全的(进程不安全),如何解决多进程的问题在logging Cookbook(右边的红框)中有详细的介绍,也可以引入第三方包解决,后面会简单介绍下。
Basic Tutorial中有些基本的使用方法,Advanced Logging Tutorial中的LoggingFlow详细介绍了log日志信息的处理流程,简单理解就是logger负责生产日志,handle负责记录日志。
logging模块的使用有两种,一种是直接使用,一种是通过配置(python类或者是配置文件的方式这里归为一类)。这两种方法使用起来各有优点,直接使用配置会灵活些,创建响应的实例后可以直接修改属性,比如namer和suffix参数等等;配置文件的使用方式比较优雅,如果想做一些个性化的配置会比较麻烦些,可以自己创建对应的类来实现。
1、直接配置使用
2、使用配置文件
使用配置文件也有几种不同的格式,这里主要是想介绍字典类型的dictConfig(该例子在Logging Cookbook)的使用,你直接使用图片中的代码是会报错的,需要把里面的owner字段去掉(你的电脑可能并不存在这两个用户)。
这里还要额外说明下dicConfig()的参数要求,这些都可以在官方文档中找到,这里简单介绍下主要的几个点。
logging.config — Logging configuration — Python 3.10.5 documentation
1、version目前有效值为1(必要参数)。
后面的都是可选参数了:
2、‘()’代表自定义方法(实例)。
3、formatters格式化中定义日志相关的记录格式。
4、filters过滤器中定义日志过滤的条件,确认哪些日志需要保留。
5、handlers处理器中定义日志的输出方式,比如console控制台或者file文件以及输出日志的格式(format)。
6、loggers记录器中定义日志级别、过滤器以及日志处理器(handlers)。
7、root中进行根logger的配置,其配置可以被其他logger使用,可以理解为是父logger,其他的logger可以继承这个root的配置。所有的日志记录都会转发到root,如果logger中有等级配置,则这个转发的日志等级和root中的配置无关(root提供配置的继承)。由于有继承关系的存在,在大部分情况下同一个应用中并没有必要配置所有的logger。
8、incremental中进行额外配置的添加默认False。
9、disable_existing_loggers 中设置非root-loggers的可用性(不包括loggingConfigure中配置的loggers),不配置默认为True。
logger中日志的级别具有几个特点:1、可以继承父类的日志级别。2、配置的日志级别是大于关系,配置了DEBUG实际是配置了大于DEBUG的日志级别,如果要精确输出某种日志级别需要单独实现。
注意:采用配置文件的方式时要注意,配置文件中自定义函数的参数一定要是初始化函数的入参,属性或者方法并不支持直接在配置文件中配置。
附上一个配置示例,配置文件中添加了输出到console的配置(如果是debug状态需要输出到console)
import logging, logging.config, os
import time
import socket
from logging.handlers import TimedRotatingFileHandler
import logging.config
host_name = socket.gethostname()
sys_name = 'system_name'
# 获取当前脚本绝对路径
sys_path = os.path.abspath(__file__)
def owned_file_handler(log_level):
# 获取当前路径作为日志存放路径
log_dir = os.path.join(os.path.dirname(sys_path), 'logs')
# 如果路径不存在创建对应路径
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 文件命名规则
file_name = os.path.join(log_dir, '{}_{}_{}.log'.format(sys_name, log_level, host_name))
# 滚动备份7个日志每天凌晨进行备份
# file_handler = TimedRotatingFileHandler(file_name, when='midnight', backupCount=7, interval=1, encoding='utf-8')
# 这里为了测试间隔1秒保存两个日志文件
file_handler = TimedRotatingFileHandler(file_name, when='S', backupCount=2, interval=1, encoding='utf-8')
# 配置日志文件后缀,如果想使用默认的数量管理对日志进行管理,日志日期的后缀格式为S'%Y-%m-%d_%H-%M-%S'
# file_handler.suffix = '%Y-%m-%d_%H-%M-%S.log'
return file_handler
# 添加debug函数用于调试的时候可以输出到console
def get_debug(debug):
file_filter = logging.Filter()
file_filter.filter = lambda debug: True
return file_filter
# 添加日志级别的过滤
def owned_filter(log_levelno):
file_filter = logging.Filter()
file_filter.filter = lambda record: record.levelno == log_levelno
return file_filter
LOGGING_SET = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
# 设置日志格式以及时间格式
'format': '[%(asctime)s:%(msecs)03d][{}][{}][%(module)s][%(process)d][%(thread)d]'
'----[%(levelname)s]---------%(message)s'.format(host_name, sys_name),
'datefmt': '%Y-%m-%d-%H-%M-%S'
},
},
'filters': {
# 这里根据是否需要输出到console的实际使用情况进行配置
'require_debug_true': {
'()': get_debug,
'debug': True
},
# 配置INFO级别的filter
'filter_info': {
'()': owned_filter,
'log_levelno': logging.INFO
},
# 配置ERROR级别的filter
'filter_error': {
'()': owned_filter,
'log_levelno': logging.ERROR
}
},
'handlers': {
# 配置console输出
'console': {
'class': 'logging.StreamHandler',
'formatter': 'default',
'filters': ['require_debug_true'],
},
# 配置INFO级别的文件输出
'file_info': {
'()': owned_file_handler,
'log_level': 'INFO',
'formatter': 'default',
'filters': ['filter_info']
},
# 配置ERROR级别的文件输出
'file_error': {
'()': owned_file_handler,
'log_level': 'ERROR',
'formatter': 'default',
'filters': ['filter_error']
}
},
# root可以获取所有日志信息,配置输出console,有必要的时候也可以配置输出到文件
'root': {
'handlers': ['console'],
'level': 'DEBUG'
},
# 定义使用的logger:own_logger
'loggers': {
'own_logger': {
'handlers': ['file_info', 'file_error'],
},
}
}
logging.config.dictConfig(LOGGING_SET)
own_logger = logging.getLogger('own_logger')
if __name__ == '__main__':
for i in range(10):
own_logger.debug('A debug message' * 10)
own_logger.info('A info message' * 10)
own_logger.error('A error message' * 10)
time.sleep(1)