Python进阶教程m14–日志记录logging

原文链接:http://www.juzicode.com/python-tutorial-logging/

在调试代码时通常使用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()方法:

级别等效数值
CRITICAL50
ERROR40
WARNING30
INFO20
DEBUG10
NOTSET0

如果要修改日志等级,可以使用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)slogger的名字

创建控制台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'
正常结束 

相关文章:

1、Python调试神器–PySnooper

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注