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 判断一个进程是否存在
Apr 09 Python
Python使用Paramiko模块编写脚本进行远程服务器操作
May 05 Python
开源Web应用框架Django图文教程
Mar 09 Python
Python实现将一个正整数分解质因数的方法分析
Dec 14 Python
Django管理员账号和密码忘记的完美解决方法
Dec 06 Python
Python Matplotlib实现三维数据的散点图绘制
Mar 19 Python
浅谈Python3中strip()、lstrip()、rstrip()用法详解
Apr 29 Python
django获取from表单multiple-select的value和id的方法
Jul 19 Python
django使用admin站点上传图片的实例
Jul 28 Python
python线程中的同步问题及解决方法
Aug 29 Python
Python如何使用正则表达式爬取京东商品信息
Jun 01 Python
Python FuzzyWuzzy实现模糊匹配
Apr 28 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 Curl出现403错误的解决办法
2014/05/29 PHP
php实现给图片加灰色半透明效果的方法
2014/10/20 PHP
getElementByIdx_x js自定义getElementById函数
2012/01/24 Javascript
javascript 进阶篇2 CSS XML学习
2012/03/14 Javascript
javascript针对DOM的应用实例(一)
2012/04/15 Javascript
jquery 之 $().hover(func1, funct2)使用方法
2012/06/14 Javascript
JQuery select控件的相关操作实现代码
2012/09/14 Javascript
让复选框只能选择一项的方法
2013/10/08 Javascript
jquery实现表格隔行换色效果
2015/11/19 Javascript
JQuery datepicker 用法详解
2015/12/25 Javascript
2016年最热门的15 款代码语法高亮工具,美化你的代码
2016/01/06 Javascript
Jquery+ajax+JAVA(servlet)实现下拉菜单异步取值
2016/03/23 Javascript
js判断PC端与移动端跳转
2020/12/24 Javascript
利用vscode编写vue的简单配置详解
2017/06/17 Javascript
在一般处理程序(ashx)中弹出js提示语
2017/08/16 Javascript
javascript 缓冲运动框架的实现
2017/09/29 Javascript
在layui中使用form表单监听ajax异步验证注册的实例
2019/09/03 Javascript
解决echarts echarts数据动态更新和dataZoom被重置问题
2020/07/20 Javascript
Python抓取百度查询结果的方法
2015/07/08 Python
pytorch 常用线性函数详解
2020/01/15 Python
Django用户身份验证完成示例代码
2020/04/03 Python
如何真正的了解python装饰器
2020/08/14 Python
Django Model层F,Q对象和聚合函数原理解析
2020/11/12 Python
python读写数据读写csv文件(pandas用法)
2020/12/14 Python
捷克体育用品购物网站:D-sport
2017/12/28 全球购物
办公室主任职责范文
2013/11/08 职场文书
大学生优秀班干部事迹材料
2014/05/26 职场文书
技术经济专业求职信
2014/09/03 职场文书
学校组织向国旗敬礼活动方案(中小学适用)
2014/09/27 职场文书
单位实习工作证明怎么写
2014/11/02 职场文书
导游词开场白
2015/01/31 职场文书
收入证明范本
2015/06/12 职场文书
运动会通讯稿100字
2015/07/20 职场文书
Python 数据科学 Matplotlib图库详解
2021/07/07 Python
JVM钩子函数的使用场景详解
2021/08/23 Java/Android
FFmpeg视频处理入门教程(新手必看)
2022/01/22 杂记