Python logging正在忠实记录你的一举一动(日志记录模块logging)
原文链接:http://www.juzicode.com/archives/4249
在调试代码时通常使用print()将要观察的对象打印出来,但是print()函数是一种”运行时”工具,需要实时观察对象的输出,当程序部署到生产环境后,使用print()函数就不太现实了:程序运行窗口可能会崩溃导致记录的信息丢失;简单使用print()函数不能记录时间线,多事件分析时没办法观察先后顺序;如果程序是被其他进程启动的,控制台窗口可能就不存在。Python自带的logging模块可以很好的实现输出日志的功能,既可以用来实时调试也可以输出日志到文件。
1、输出日志到控制台
先看一个简单的例子,该例子直接调用logging的各种等级日志输出函数
import logging
logging.critical('这是logging测试例子')
logging.error('VX:桔子code')
logging.warning('www.juzicode.com')
logging.info('are you kiding me?')
logging.debug('i am serious!')
运行结果:
CRITICAL:root:这是logging测试例子
ERROR:root:VX:桔子code
WARNING:root:www.juzicode.com
从结果看,在没有设置打印等级时,默认情况下WANING(logging.warning()方法)及以上等级才会打印出来。
logging中将日志分为6个等级,分别对应小写的同名方法,比如INFO对应info()方法:
级别 | 等效数值 |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
如果要修改日志等级,可以使用logging.basicConfig(level=logging.DEBUG)的方式修改:
import logging
logging.basicConfig(level=logging.DEBUG)#增加一行修改日志等级
logging.critical('这是logging测试例子')
logging.error('VX:桔子code')
logging.warning('www.juzicode.com')
logging.info('are you kiding me?')
logging.debug('i am serious!')
运行结果:
CRITICAL:root:这是logging测试例子
ERROR:root:VX:桔子code
WARNING:root:www.juzicode.com
INFO:root:are you kiding me? #WARNING以下等级的日志有输出
DEBUG:root:i am serious!
2、输出日志到文件
logging除了可以输出日志到控制台,也可以将日志写入到文件,下面是一个简单的写文件的例子:
import logging
logging.basicConfig(filename='log-example.txt',level=logging.INFO)
logging.critical('这是logging测试例子')
logging.error('VX:桔子code')
logging.warning('www.juzicode.com')
logging.info('are you kiding me?')
logging.debug('i am serious!')
该例子将不会在控制台打印任何信息,但是会将日志信息写入到文件中,这里设置了日志等级为INFO,所以logging.debug()函数输出的信息将不会被记录,打开log-example.txt文件可以看到INFO级别以上的日志被记录下来:
CRITICAL:root:这是logging测试例子
ERROR:root:VX:桔子code
WARNING:root:www.juzicode.com
INFO:root:are you kiding me?
3、同时输出日志到控制台和文件
前面的例子只能输出日志到控制台或者只能写入文件,下面介绍的方法二者兼而有之。
首先创建一个logger实例:
#创建logger实例
logger = logging.getLogger('logtop')
#设置logger实例的等级
logger.setLevel(logging.DEBUG)
这里创建了一个名称为”logtop”的logger实例,该名称后续可以用来扩展下级实例。
logger.setLevel()设置日志等级,该优先级高于其他handler的等级设置,比如在这里设置为ERROR等级,handler即使设置了INFO或者DEBUG等级,在ERROR等级以下的日志不会输出。
设置日志输出格式:
#创建formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s')
输出格式为多个 “%(属性)格式化方法 ”的形式,常用组合有:
属性 | 格式 | 备注 |
asctime | %(asctime)s | 时间,默认格式为2013-01-01 16:49:45,896 |
filename | %(filename)s | 文件名称 |
funcName | %(funcName)s | 函数名称 |
levelname | %(levelname)s | 告警等级:’DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’ |
levelno | %(levelno)s | 告警等级数值表示 |
lineno | %(lineno)d | 源码行号 |
message | %(message)s | 日志消息 |
name | %(name)s | logger的名字 |
创建控制台handler,设置控制台handler日志等级、格式:
#创建控制台handler
cons_handler = logging.StreamHandler()
cons_handler.setLevel(logging.INFO)
cons_handler.setFormatter(formatter)
创建文件handler,设置文件handler日志等级、格式:
#创建文件handler
file_handler = logging.FileHandler('log-example2.txt')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
添加handler到logger:
#添加handler到logger
logger.addHandler(cons_handler)
logger.addHandler(file_handler)
使用logger.debug()等方法输出日志:
#这里用logger实例输出日志
logger.critical('这是logging测试例子')
logger.error('VX:桔子code')
logger.warning('www.juzicode.com')
logger.info('are you kiding me?')
logger.debug('i am serious!')
完整的例子:
#创建logger实例
logger = logging.getLogger('logtop')
#设置logger实例的等级
logger.setLevel(logging.DEBUG)
#创建formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - % (lineno)d - %(levelname)s - %(message)s')
#创建控制台handler
cons_handler = logging.StreamHandler()
cons_handler.setLevel(logging.INFO)
cons_handler.setFormatter(formatter)
#创建文件handler
file_handler = logging.FileHandler('log-example2.txt')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
#添加handler到logger
logger.addHandler(cons_handler)
logger.addHandler(file_handler)
#这里用logger实例输出日志
logger.critical('这是logging测试例子')
logger.error('VX:桔子code')
logger.warning('www.juzicode.com')
logger.info('are you kiding me?')
logger.debug('i am serious!')
因为控制台设置的等级为INFO,logger.debug()方法输出不会显示,运行结果:
2021-03-31 22:02:47,850 - logtop - 31 - CRITICAL - 这是logging测试例子
2021-03-31 22:02:47,851 - logtop - 32 - ERROR - VX:桔子code
2021-03-31 22:02:47,851 - logtop - 33 - WARNING - www.juzicode.com
2021-03-31 22:02:47,852 - logtop - 34 - INFO - are you kiding me?
这里需要注意的是如果用logger.setLevel(logging.ERROR),其他不做变化,因为该设置等级优先级更高,会屏蔽掉cons_handler.setLevel(logging.INFO)和file_handler.setLevel(logging.DEBUG)的设置,只有ERROR级别的日志才会输出到控制台和文件,运行结果就会是这样子的:
2021-03-31 22:06:17,007 - logtop - 31 - CRITICAL - 这是logging测试例子
2021-03-31 22:06:17,008 - logtop - 32 - ERROR - VX:桔子code
4、使用二级实例
前面的例子中创建了一个名称为”logtop”的实例,可以用“logtop.xyz”的方式从”logtop”创建一个下级实例,它会继承”logtop”的属性,不需要再重新设置日志等级、输出格式、创建handler就可以直接使用。在前面的例子中增加2行:
logger_lv2 = logging.getLogger('logtop.lv2')
logger_lv2.warning('www.juzicode.com')
运行结果:
2021-03-31 22:33:23,386 - logtop - 31 - CRITICAL - 这是logging测试例子 #logtop的日志
2021-03-31 22:33:23,386 - logtop - 32 - ERROR - VX:桔子code
2021-03-31 22:33:23,386 - logtop - 33 - WARNING - www.juzicode.com
2021-03-31 22:33:23,386 - logtop - 34 - INFO - are you kiding me?
2021-03-31 22:33:23,386 - logtop.lv2 - 38 - WARNING - www.juzicode.com #logtop.lv2的日志
5、记录异常
通过设置exc_info=True,可以在发生异常时在except中捕获并记录,仍然使用前面创建和配置的logger实例:
try:
a = int(input('输入要转换的值: '))
print('a:',a)
except:
logger.error('捕获到异常:',exc_info=True)
print('正常结束')
当输入不能转换为10进制的字符时,运行结果:
输入要转换的值: xyz
2021-03-31 22:35:55,990 - logtop - 44 - ERROR - 捕获到异常:
Traceback (most recent call last):
File "logging-stdout-file-catch-except.py", line 41, in <module>
a = int(input('输入要转换的值: '))
ValueError: invalid literal for int() with base 10: 'xyz'
正常结束