一般的小代码,初学者可能会采取在适当的地方添加print()方法输出信息来进行代码的检查和调试,但是如果代码比较多,print()方法在调试完成后需要将所有的print()代码都注释或者删除以使得调试输出信息不再输出,这对于大代码非常不方便。因此最好不要养成使用print()语句调试的习惯,一开始就学习使用logging模块debug方法来调试代码。
python系统自带logging模块可以实现对代码的运行追踪的日志记录功能,提供了便捷的代码bug追踪机制,不仅可以将日志打印出来,还可以将日志保存为文档,便于保存与查看。开发人员可以对他们的代码添加日志调用,借此来指示某事件的发生。一个事件通过一些包含变量数据的描述信息来描述(比如:每个事件发生时的数据都是不同的)。logging模块还提供区分事件的重要性的不同级别:
级别 |
级别数值 |
使用时机 |
DEBUG |
10 |
详细信息,常用于调试。 |
INFO |
20 |
程序正常运行过程中产生的一些信息。 |
WARNING |
30 |
警告用户,虽然程序还在正常工作,但有可能发生错误。 |
ERROR |
40 |
由于更严重的问题,程序已不能执行一些功能了。 |
CRITICAL |
50 |
严重错误,程序已不能继续运行。 |
logging的默认的级别是WARNING,意味着只会追踪该级别及以上的事件,DEBUG和INFO会被忽略,WARING、ERROR和CRITICAL会被记录,除非更改日志配置。
一个简单的将结果输出到控制台的语句为:
import logging
logging.warning('info level') # WARNING:root:info levelb
有多种方法用来处理被跟踪的事件。最简单的方法就是把它们打印到终端控制台上:
import logging
from logging import debug
logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level = logging.DEBUG)
def func(s):
str1 = 'Loop'
str2 = 'leap'
debug(f's的数据类型为 {type(s)}, {s}')
debug(f'{str1} before you {str2}')
return s/10
if __name__ == '__main__':
func(100)
输出结果为:
DEBUG:func:s的数据类型为 <class 'int'>, 100
DEBUG:func:Loop before you leap
basicConfig方法介绍
在使用过程中可以直接用logging.debug()来替换print()。如果程序调试好,可以通过修改level来控制debug info的输出, eg:将以下这句的level = logging.DEBUG换为logging.INFO就可以了:
logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level = logging.INFO)
basicConfig方法中的format定义了日志信息中包含的信息的格式,变量是logging内置的keys:
属性 |
格式 |
描述 |
asctime |
%(asctime)s |
日志产生的时间,默认格式为2003-07-08 16:49:45,896 |
created |
%(created)f |
time.time()生成的日志创建时间戳 |
funcName |
%(funcName)s |
调用日志的函数名 |
levelname |
%(levelname)s |
日志级别 (‘DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’) |
lineno |
%(lineno)d |
日志所针对的代码行号(如果可用的话) |
module |
%(module)s |
生成日志的模块名 |
message |
%(message)s |
具体的日志信息 |
name |
%(name)s |
日志调用者 |
我们还可以通过basicConfig方法中的参数设置,将结果输出到文件:
import logging
logging.basicConfig(filename='test.log', level=20)
logging.log(10, '级别为10的一条日志')
logging.log(20, '级别为20的一条日志')
上例中,通过basicConfig方法将日志输出到test.log文件,并且设置只有级别大于等于20的才会写入到该日志文件。也就是说,上例中,第一条级别为10的日志将不会写入到文件。并且,需要注意的是,如果你查看日志文件,如果出现乱码的话,请检查编码方式。
上例中,在basicConfig方法中,级别20也可以这样指定:
logging.basicConfig(filename='test.log', level=logging.INFO)
logging.debug('debug level: 10')
logging.info('info level: 20')
logging.warning('warning level: 30')
logging.error('error level: 40')
logging.critical('critical level: 50')
上例中同样只有级别大于等于20的将会被写入文件test.log。
basicConfig方法还可以指定一下参数:
- filemode,日志文件写入方式,可以是w和a,默认的是a模式。
- datefmt,指定时间的输出格式。
- level,指定日志输出的类别,程序会输出大于等于此级别的信息。
- stream,在没有指定filename的时候会默认使用StreamHandler,这时stream可以指定初始化的文件流。
- handlers:可以指定日志处理时所使用的 Handlers,必须是可迭代的。
示例:
import logging
logging.basicConfig(
filename='test.log',
filemode='w',
level=logging.DEBUG,
datefmt='%Y/%m/%d %H:%M:%S',
format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s'
)
logging.debug('debug level: 10')
logging.info('info level: 20')
logging.warning('warning level: 30')
logging.error('error level: 40')
logging.critical('critical level: 50')
输出结果为:
2019/05/30 14:38:09 - root - DEBUG - 36 - 日志模块 - debug level: 10
2019/05/30 14:38:09 - root - INFO - 37 - 日志模块 - info level: 20
2019/05/30 14:38:09 - root - WARNING - 38 - 日志模块 - warning level: 30
2019/05/30 14:38:09 - root - ERROR - 39 - 日志模块 - error level: 40
2019/05/30 14:38:09 - root - CRITICAL - 40 - 日志模块 - critical level: 50
需要注意的是,logging.basicConfig只生效一次,比如:
···
import logging
logging.basicConfig(
filename=‘test1.log’,
filemode=‘w’,
level=logging.DEBUG
)
无效
logging.basicConfig(
filename=‘test2.log’,
filemode=‘a’,
level=logging.INFO
)
logging.debug(‘debug level: 10’)
logging.info(‘info level: 20’)
logging.warning(‘warning level: 30’)
logging.error(‘error level: 40’)
logging.critical(‘critical level: 50’)
···
正如上例所示,我们配置了两次basicConfig。但如果运行你会发现,只有第一个配置生效了,第二个配置不会生效。原因是当在第一次配置的时候,logging在内部就会进行配置,第二次再次配置的时候,logging就会认为我已经配置好了,不需要再次配置了。
Debug 简易使用
首先使用logging.getLogger()进行配置,参数为文件名,通过logger.setLevel()设置logger的级别:
import logging
# 配置logger并设置等级为DEBUG
logger = logging.getLogger('logging_debug')
logger.setLevel(logging.DEBUG)
然后使用logging.StreamHandler()方法初始化consoleHandler,通过consoleHandler.setLevel()设置级别:
# 配置控制台Handler并设置等级为DEBUG
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
再通过logger.addHandler()方法将consoleHandler加入logger,完成logger的配置:
logger.addHandler(consoleHandler)
完整代码:
import logging
# 配置logger并设置等级为DEBUG
logger = logging.getLogger('logging_debug')
logger.setLevel(logging.DEBUG)
# 配置控制台Handler并设置等级为DEBUG
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
# 将Handler加入logger
logger.addHandler(consoleHandler)
logger.debug('This is a logging.debug')
输出结果:
This is a logging.debug