Python中使用logging模块代替print(logging简明指南)


Posted in Python onJuly 09, 2014

替换print?print怎么了?

print 可能是所有学习Python语言的人第一个接触的东西。它最主要的功能就是往控制台 打印一段信息,像这样:

print 'Hello, logging!'

print也是绝大多数人用来调试自己的程序用的最多的东西,就像写js使用 console.log 一样那么自然。很多刚刚开始学习Python的新手甚至有一定经验的老手,都在使用print 来调试他们的代码。

比如这是一个我写的输出 斐波那契数列 的小程序,让我们来看看它的代码:

# -*- coding: utf-8 -*-

"""

A simple fibonacci program

"""

import argparse
parser = argparse.ArgumentParser(description='I print fibonacci sequence')

parser.add_argument('-s', '--start', type=int, dest='start',

                    help='Start of the sequence', required=True)

parser.add_argument('-e', '--end', type=int, dest='end',

                    help='End of the sequence', required=True)
def infinite_fib():

    a, b = 0, 1

    yield a

    yield b

    while True:

        #print 'Before caculation: a, b = %s, %s' % (a, b)

        a, b = b, a + b

        #print 'After caculation: a, b = %s, %s' % (a, b)

        yield b


def fib(start, end):

    for cur in infinite_fib():

        #print 'cur: %s, start: %s, end: %s' % (cur, start, end)

        if cur > end:

            return

        if cur >= start:

            #print 'Returning result %s' % cur

            yield cur
def main():

    args = parser.parse_args()

    for n in fib(args.start, args.end):

        print n,
if __name__ == '__main__':

    main()

让我们来看看它工作的怎么样:

$ python fib.py  -s 1 -e 100

1 1 2 3 5 8 13 21 34 55 89

$ python fib.py  -s 100 -e 1000

144 233 377 610 987

没有任何问题,程序正确的完成了它的功能。但等等, 程序里面的那一堆被注释掉的print语句是怎么回事?

原来,这是我编写这个小程序的过程中,用来 调试(DEBUG) 的输出信息,在我完成了这 个程序以后,我自然就把这些print给注释掉了。让我们来看看如果把这个print语句打开后结果会怎么样?

$ python fib.py  -s 1 -e 100

cur: 0, start: 1, end: 100

cur: 1, start: 1, end: 100

Returning result 1

1 Before caculation: a, b = 0, 1

After caculation: a, b = 1, 1

cur: 1, start: 1, end: 100

... ...

... ...

(不计其数的输出信息)

如你所见,所有的计算过程都被打印出来了。

写的时候加上print,提交代码的时候还得记得把print语句删掉/注释掉,为什么我们要忍受这样的麻烦事呢? 让我们来介绍我们的主角 logging ,它几乎就是为这种使用情景而生的。

更好的做法,使用logging模块

logging模块是Python内置的日志模块,使用它可以非常轻松的处理和管理日志输出。 logging模块最简单的用法,是直接使用basicConfig方法来对logging进行配置:

import logging
# 设置默认的level为DEBUG

# 设置log的格式

logging.basicConfig(

    level=logging.DEBUG,

    format="[%(asctime)s] %(name)s:%(levelname)s: %(message)s"

)
# 记录log

logging.debug(...)

logging.info(...)

logging.warn(...)

logging.error(...)

logging.critical(...)

这样配置完logging以后,然后使用``logging.debug``来替换所有的print语句就可以了。 我们会看到这样的输出:

[2014-03-18 15:17:45,216] root:cur: 0, start: 1, end: 100

[2014-03-18 15:17:45,216] root:DEBUG: cur: 1, start: 1, end: 100

[2014-03-18 15:17:45,216] root:DEBUG: Returning result 1

[2014-03-18 15:17:45,216] root:DEBUG: Before caculation: a, b = 0, 1

... ...

使用真正的logger

上面说的basicConfig方法可以满足你在绝大多数场景下的使用需求,但是basicConfig有一个 很大的缺点。

调用basicConfig其实是给root logger添加了一个handler,这样当你的程序和别的使用了 logging的第三方模块一起工作时,会影响第三方模块的logger行为。这是由logger的继承特性决定的。

所以我们需要使用真正的logger:

import logging
# 使用一个名字为fib的logger

logger = logging.getLogger('fib')
# 设置logger的level为DEBUG

logger.setLevel(logging.DEBUG)
# 创建一个输出日志到控制台的StreamHandler

hdr = logging.StreamHandler()

formatter = logging.Formatter('[%(asctime)s] %(name)s:%(levelname)s: %(message)s')

hdr.setFormatter(formatter)
# 给logger添加上handler

logger.addHandler(hdr)

这样再使用logger来进行日志输出就行了。不过这样的坏处就是代码量比basicConfig要大不少。 所以我建议如果是非常简单的小脚本的话,直接使用basicConfig就可以,如果是稍微大一些 项目,建议认真配置好logger。

动态控制脚本的所有输出

使用了logging模块以后,通过修改logger的log level,我们就可以方便的控制程序的输出了。 比如我们可以为我们的斐波那契数列添加一个 -v 参数,来控制打印所有的调试信息。

# 添加接收一个verbose参数

parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',

                    help='Enable debug info')
# 判断verbose

if args.verbose:

    logger.setLevel(logging.DEBUG)

else:

    logger.setLevel(logging.ERROR)

这样,默认情况下,我们的小程序是不会打印调试信息的,只有当传入`-v/--verbose`的时候, 我们才会打印出额外的debug信息,就像这样:

$ python fib.py  -s 1 -e 100

1 1 2 3 5 8 13 21 34 55 89
$ python fib.py  -s 1 -e 100 -v

[2014-03-18 15:17:45,216] fib:DEBUG: cur: 0, start: 1, end: 100

[2014-03-18 15:17:45,216] fib:DEBUG: cur: 1, start: 1, end: 100

[2014-03-18 15:17:45,216] fib:DEBUG: Returning result 1

[2014-03-18 15:17:45,216] fib:DEBUG: Before caculation: a, b = 0, 1

... ...

如你所见,使用了logging以后,什么时候需要打印DEBUG信息,什么时候需要关闭, 一切变的无比简单。

所以,赶紧用logging替换掉你的脚本里的print吧!

延伸阅读

以上这些只是介绍了logging模块最简单的一些功能,作为print的替代品来使用,logging 模块还有很多非常强大好用的功能,比如从文件读取配置、各种各样的Handlers等等。 建议阅读一下logging的官方文档:

1.logging Logging facility for Python
2.Logging HOWTO

最后附上使用logging模块的斐波那契数列程序完整代码:

# -*- coding: utf-8 -*-

"""

A simple fibonacci program

"""

import argparse
parser = argparse.ArgumentParser(description='I print fibonacci sequence')

parser.add_argument('-s', '--start', type=int, dest='start',

                    help='Start of the sequence', required=True)

parser.add_argument('-e', '--end', type=int, dest='end',

                    help='End of the sequence', required=True)

parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',

                    help='Enable debug info')
import logging
logger = logging.getLogger('fib')

logger.setLevel(logging.DEBUG)
hdr = logging.StreamHandler()

formatter = logging.Formatter('[%(asctime)s] %(name)s:%(levelname)s: %(message)s')

hdr.setFormatter(formatter)
logger.addHandler(hdr)


def infinite_fib():

    a, b = 0, 1

    yield a

    yield b

    while True:

        logger.debug('Before caculation: a, b = %s, %s' % (a, b))

        a, b = b, a + b

        logger.debug('After caculation: a, b = %s, %s' % (a, b))

        yield b


def fib(start, end):

    for cur in infinite_fib():

        logger.debug('cur: %s, start: %s, end: %s' % (cur, start, end))

        if cur > end:

            return

        if cur >= start:

            logger.debug('Returning result %s' % cur)

            yield cur
def main():

    args = parser.parse_args()

    if args.verbose:

        logger.setLevel(logging.DEBUG)

    else:

        logger.setLevel(logging.ERROR)
    for n in fib(args.start, args.end):

        print n,
if __name__ == '__main__':

    main()
Python 相关文章推荐
Python实现拷贝多个文件到同一目录的方法
Sep 19 Python
django利用request id便于定位及给日志加上request_id
Aug 26 Python
python中协程实现TCP连接的实例分析
Oct 14 Python
python找出一个列表中相同元素的多个索引实例
Jun 11 Python
Python 进程操作之进程间通过队列共享数据,队列Queue简单示例
Oct 11 Python
Pandas时间序列:重采样及频率转换方式
Dec 26 Python
python math模块的基本使用教程
Jan 16 Python
Python使用tkinter实现小时钟效果
Feb 22 Python
Python源码解析之List
May 21 Python
Python爬取用户观影数据并分析用户与电影之间的隐藏信息!
Jun 29 Python
分享提高 Python 代码的可读性的技巧
Mar 03 Python
Python函数对象与闭包函数
Apr 13 Python
Python中的魔法方法深入理解
Jul 09 #Python
gearman的安装启动及python API使用实例
Jul 08 #Python
python实现跨文件全局变量的方法
Jul 07 #Python
Python中的并发编程实例
Jul 07 #Python
Python编程语言的35个与众不同之处(语言特征和使用技巧)
Jul 07 #Python
python基于mysql实现的简单队列以及跨进程锁实例详解
Jul 07 #Python
python中使用urllib2获取http请求状态码的代码例子
Jul 07 #Python
You might like
PHP集成FCK的函数代码
2008/09/27 PHP
PHP JSON 数据解析代码
2010/05/26 PHP
php定时删除文件夹下文件(清理缓存文件)
2013/01/23 PHP
PHP框架Laravel的小技巧两则
2015/02/10 PHP
php curl中gzip的压缩性能测试实例分析
2016/11/08 PHP
tp5.1框架数据库子查询操作实例分析
2020/05/26 PHP
javascript时区函数介绍
2012/09/14 Javascript
在jQuery中 常用的选择器介绍
2013/04/16 Javascript
Javascript中this的用法详解
2014/09/22 Javascript
jQuery图片特效插件Revealing实现拉伸放大
2015/04/22 Javascript
nodejs 中模拟实现 emmiter 自定义事件
2016/02/22 NodeJs
JavaScript String 对象常用方法详解
2016/05/13 Javascript
jquery遍历table的tr获取td的值实现方法
2016/05/19 Javascript
谈谈PHP中相对路径的问题与绝对路径的使用
2016/08/16 Javascript
jQuery解析XML 详解及方法总结
2016/09/28 Javascript
移动端界面的适配
2017/01/11 Javascript
解决Vue 项目打包后favicon无法正常显示的问题
2018/09/01 Javascript
基于js判断浏览器是否支持webGL
2020/04/18 Javascript
vue实现div可拖动位置也可改变盒子大小的原理
2020/09/16 Javascript
原生js实现分页效果
2020/09/23 Javascript
python中mechanize库的简单使用示例
2014/01/10 Python
在Python 3中实现类型检查器的简单方法
2015/07/03 Python
详解Python爬虫的基本写法
2016/01/08 Python
在Python 2.7即将停止支持时,我们为你带来了一份python 3.x迁移指南
2018/01/30 Python
使用Python3+PyQT5+Pyserial 实现简单的串口工具方法
2019/02/13 Python
django模板结构优化的方法
2019/02/28 Python
python+selenium定时爬取丁香园的新型冠状病毒数据并制作出类似的地图(部署到云服务器)
2020/02/09 Python
python读取xml文件方法解析
2020/08/04 Python
Python安装Bs4的多种方法
2020/11/28 Python
用C语言实现文件读写操作
2013/10/27 面试题
深圳茁壮笔试题
2015/05/28 面试题
八一建军节主持词
2015/07/01 职场文书
tensorflow中的数据类型dtype用法说明
2021/05/26 Python
pandas中关于apply+lambda的应用
2022/02/28 Python
Python OpenGL基本配置方式
2022/05/20 Python
sql查询语句之平均分、最高最低分及排序语句
2022/05/30 MySQL