在项目开发中,日志信息是程序中必不可少的组成部分。每一种语言都有相应的日志模块,如java中log4j,而python中是通过logging模块来提供日志功能。
1.日志要哪些本质功能?
在分享日志logging模块之前,先来思考下我们需要哪些的日志信息?或者日志信息给我们提供什么样的价值?
- 基本功能1.首先:日志应该包含程序运行的基本信息,比如运行时间,运行的位置信息(哪个文件,哪行)等,这些信息可以帮助我们观测程序的运行的基本情况,以便更好的分析你需要的输出的信息所代表的含义;
- 基本功能2.接着:日志最重要的功能是在程序功能中输出一些信息数据,以便更好的了解程序的运行情况。而我们需要输出的信息太多了,在不同的开发阶段需要的信息也不同,如一些流程必要的基本信息,调试的过程信息,错误信息和告警信息;这么多信息要展示,日志信息会显得杂乱,不能突出重点,同时这些信息又都能在不同场景使用(通常我们在调试完程序后,调试一些信息可以删除,但是出现bug后,又有调试的时候,就会出现重复劳动的问题);所以日志模块需要提供日志筛选和过滤的功能,通常是对日志信息进行等级分类,并设置需要显示的最低等级。这样我们需要调试的时候就开启调试的日志,不需要的时候开启更高等级的日志信息,更加灵活
- 基本功能3.另外,日志应该支持输出到多种途径,比如直接终端显示,一般线上系统需要输出到日志文件,便于排查问题
以上就是日志必须要有的功能3个模块。
2.logging基本使用
python的logging模块也提供了上面说的3个基本模块,也是我们最经常使用的(别的花哨的功能可以查看API咯);
import logging
logging.basicConfig()
print(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.debug("this is a debug")
logger.info('this is a info')
logger.warning('this is a warn')
logger.critical('this is a critical')
logger.error('this is a error')
# __main__
# DEBUG:__main__:this is a debug
# INFO:__main__:this is a info
# WARNING:__main__:this is a warn
# CRITICAL:__main__:this is a critical
# ERROR:__main__:this is a error
logging中的日志等级可以分为5个,从低到高分别是:
- DEBUG:调试
- INFO: 基本信息
- WARNING: 非错误,警告类信息
- ERROR: 异常错误信息
- CRITICAL:严重问题如硬件问题等,基本很少使用
默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
上面我们了解了logging的基本的使用:
- 导入logging, 默认情况下回进行基本配置
- 获取日志记录器logger
- 设置日志等级
- 利用日志记录器logger进行日志输出
3.logging进阶
logging主要由四个模块组成:
- 记录器 loggers: 记录日志主要信息,一般通过logger.getLogger("name"), 可以设置名字来创建不同日志记录器,默认不设置为root记录器,记录器可以设置日志等级,对应日志基本功能2
- 句柄 handlers; 设置日志的输出的形式,正常是文件输出和屏幕流输出,对应日志基本功能3
- 过滤器 Filters:很少用,过滤日志信息
- 格式化Formatters:设置日志输出的格式(如时间,文件名等), 对应日志基本功能1
下面给出完整的例子:
import logging
def get_root_logger(logger_name='log', log_level=logging.INFO, log_file=None):
logger = logging.getLogger(logger_name)
logger.setLevel(level=log_level)
format_str = '%(asctime)s [ %(name)s ] %(filename)s - line:%(lineno)d %(levelname)s: %(message)s'
# if the logger has been initialized, just return it
if logger.hasHandlers():
return logger
if log_file is not None:
file_handler = logging.FileHandler(log_file, 'w')
file_handler.setFormatter(logging.Formatter(format_str))
file_handler.setLevel(log_level)
logger.addHandler(file_handler)
else:
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter(format_str))
sh.setLevel(log_level)
logger.addHandler(sh)
return logger
logger = get_root_logger("mylog")
logger.info('this is a info')
logger.warning('this is a warn')
logger.critical('this is a critical')
logger.error('this is a error')
# 2022-09-10 22:47:08,611 [ mylog ] log.py - line:24 INFO: this is a info
# 2022-09-10 22:47:08,611 [ mylog ] log.py - line:25 WARNING: this is a warn
# 2022-09-10 22:47:08,611 [ mylog ] log.py - line:26 CRITICAL: this is a critical
# 2022-09-10 22:47:08,614 [ mylog ] log.py - line:27 ERROR: this is a error
从上面可以知道,handler需要设置日志格式和日志等级。
基本格式:
- asctime 时间
- name 日志名称
- filename 日志信息所在文件
- lineno: 日志信息坐在文件行号
- levelname:日志等级
- message 日志内容
4.logging注意事项
- 单例模式
logging.getLogger是单例模式,所以同一个名字的logger在不同的文件或者模块中使用都是调用的是同一个logger。这个在多模块中使用通过一个logger特别实用。
- 避免多个句柄输出相同的内容
由于一个logger可以有多个句柄handler,如果出现输出相同日志信息的情况,可以检查下是否多个句柄。注意logging.basicConfig()默认会创建一个屏幕流句柄。