一、日志的作用
程序和脚本往往是无人值守运行的,一旦发生问题,就需要我们去追溯当时的情况来定位问题的原因。
这便需要我们在程序和脚本中引入日志的功能。
相比于print信息,使用logging日志有以下优点
可以记录输出日志的时间、文件、函数以及代码行,甚至线程名和线程号
可以分等级记录日志(调试级Debug、信息级Info、警告级Warning、错误级Error、严重错误级Critical
)
即可以实时输出到屏幕,也可以输出到文件
二、python中使用日志管理
2.1 基本使用
import logging
logging.debug('调试级别的日志')
logging.info('信息级别的日志')
logging.warning('警告级别的日志') # 或logging.warn('警告级别的日志')
logging.error('错误级别的日志')
logging.critical('严重错误级别的日志')
try:
1/0
except Exception as ex:
logging.exception(ex) # 错误级别的日志,显示多行回溯信息
运行结果:
WARNING:root:警告级别的日志
ERROR:root:错误级别的日志
CRITICAL:root:严重错误级别的日志
ERROR:root:division by zero
Traceback (most recent call last):
File "<ipython-input-5-9be264a94d56>", line 9, in <module>
1/0
ZeroDivisionError: division by zero
发现只显示warning、error、critical的日志,这是因为logging中的默认日志记录器(root logger
)的level级别是logging.WARNING
,即默认只显示警告级别以上的日志。
2.2 修改日志格式
我们可以使用logging.basicConfig
来修改root logger
的设置。
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug('调试级别的日志')
logging.info('信息基本的日志')
logging.warning('警告级别的日志')
logging.error('错误级别的日志')
logging.critical('严重错误级别的日志')
再次运行,发现所有的日志都可以输出了
level日志等级支持
- logging.NOTSET: 未设置,输出所有级别的日志
- logging.DEBUG: 调试级别,输出所有级别的日志
- logging.INFO: 信息级别,输出包含信息级别以上的日志
- logging.WARNING: 警告级别,输出包含警告级别的日志
- logging.ERROR: 错误级别,输出包含错误级别以上的日志
- logging.CRITICAL: 严重错误级别,仅输出严重错误日志
日志等级的关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET
2.3修改日志的格式
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug('调试级别的日志')
logging.info('信息基本的日志')
logging.warning('警告级别的日志')
logging.error('错误级别的日志')
logging.critical('严重错误级别的日志')
运行输出如下:
2020-11-04 10:11:49,238 - DEBUG - 调试级别的日志
2020-11-04 10:11:49,238 - INFO - 信息基本的日志
2020-11-04 10:11:49,238 - WARNING - 警告级别的日志
2020-11-04 10:11:49,238 - ERROR - 错误级别的日志
2020-11-04 10:11:49,238 - CRITICAL - 严重错误级别的日志
format中使用%(变量名)s
这样的具名占位符,来输出不同的信息,其他的字符则原样显示。
支持的变量如下:
%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号(因为是数字,所以使用%d)
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(message)s: 打印日志信息
对于日期,我们可以使用datefmt来修改,比如,将上例中的logging.basicConfig
修改为
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y年%m月%d日 %H:%M:%S')
输出形式如下:
2020年11月04日 10:19:04 - DEBUG - 调试级别的日志
2020年11月04日 10:19:04 - INFO - 信息基本的日志
2020年11月04日 10:19:04 - WARNING - 警告级别的日志
2020年11月04日 10:19:04 - ERROR - 错误级别的日志
2020年11月04日 10:19:04 - CRITICAL - 严重错误级别的日志
2.4 输出到文件
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y年%m月%d日 %H:%M:%S',
filename='run.log',
filemode='a'
)
logging.debug('调试级别的日志')
logging.info('信息基本的日志')
logging.warning('警告级别的日志')
logging.error('错误级别的日志')
logging.critical('严重错误级别的日志')
运行后,日志将不会输出到屏幕,转而输出到文件中,filemode支持'w'
每次覆盖和'a'
追加模式。
2.5 同时输出日志到文件和控制台
如果想即输出到控制台又输出到文件,我们可以在logging.basicConfig
的handlers参数中添加两个不同的handler来实现,logging.StreamHander()
可以用于输出到控制台,logging.FileHandler()
可以用于输出到文件。
import logging
cli_handler = logging.StreamHandler() # 输出到屏幕的日志处理器
file_handler = logging.FileHandler(filename='run.log', mode='a', encoding='utf-8') # 输出到文件的日志处理器
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y年%m月%d日 %H:%M:%S',
handlers=[cli_handler, file_handler] # 添加两个日志处理器
)
logging.debug('调试级别的日志')
logging.info('信息基本的日志')
logging.warning('警告级别的日志')
logging.error('错误级别的日志')
logging.critical('严重错误级别的日志')
2.6 自定义Logger
使用logging.basicConfig是直接配置全局的root logger,会对项目中所有的模块及三方包产生影响。
为了不影响其他模块和三方包的日志输出,我们可以使用自定义Logger,即日志记录器,基本步骤为
- 使用logging.getLogger()新建一个logger对象,并配置期日志等级(总日志开关)
- 新建多个日志处理器,分别设置其格式和日志等级
- 将多个日志处理器添加到logger中
代码如下:
# 文件名: mylogger.py
import logging
def get_logger(name):
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG) # 设置总日志等级
format = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y年%m月%d日 %H:%M:%S') # 日志格式
cli_handler = logging.StreamHandler() # 输出到屏幕的日志处理器
file_handler = logging.FileHandler(filename='run.log', mode='a', encoding='utf-8') # 输出到文件的日志处理器
cli_handler.setFormatter(format) # 设置屏幕日志格式
file_handler.setFormatter(format) # 设置文件日志格式
cli_handler.setLevel(logging.INFO) # 设置屏幕日志等级, 可以大于日志记录器设置的总日志等级
# file_hander.setLevel(logging.DEBUG) # 不设置默认使用logger的等级
logger.handlers.clear() # 清空已有处理器, 避免继承了其他logger的已有处理器
logger.addHandler(cli_handler) # 将屏幕日志处理器添加到logger
logger.addHandler(file_handler) # 将文件日志处理器添加到logger
return logger
使用方式为,导入本模块的get_logger方法,使用logger代替logging打印各种信息。
from mylogger import get_logger
logger = get_logger('mylogger')
logger.debug('调试级别的日志')
logger.info('信息基本的日志')
logger.warning('警告级别的日志')
logger.error('错误级别的日志')
logger.critical('严重错误级别的日志')
你好