python logging

作用:报告状态、错误和信息消息。

logging模块定义了一个标准API,用来报告应用和库的错误及状态信息。由一个标准库模块提供日志API的主要好处在于:所有Python模块都可以参与日志记录,所有一个应用的日志还可以包含来自第三方模块的消息。

 

一、示例

把INFO等级以上日志输出到控制台, DEBUG等级以上日志写入日志文件。

示例:

#encoding:utf-8

import logging

# 创建一个logger
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)

# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(fh)
logger.addHandler(ch)

# 记录一条日志
logger.debug('foo')
logger.info('foobar')

运行结果: 控制台显示一条日志:

2012-08-31 13:44:41,137 - mylogger - INFO - foobar

日志文件写入了两条日志:

2012-08-31 13:44:41,137 - mylogger - DEBUG - foo
2012-08-31 13:44:41,137 - mylogger - INFO - foobar

 

二、logging模块的API

结合上面的例子,我们说下几个最常使用的API:

logging.getLogger([name])返回一个logger实例,如果没有指定name,返回root logger。只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对应的。这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到同一个logger实例。例如在一个模块新建了一个名字为mylogger的示例,在另一个模块,只需要通过logger =logging.getLogger(‘mylogger’)得到同一个示例。

 

Logger.setLevel(lvl)设置logger的level,NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL,如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出。

 

Logger.addHandler(hdlr)logger可以雇佣handler来帮它处理日志, handler主要有以下几种:StreamHandler: 输出到控制台FileHandler:   输出到文件handler还可以设置自己的level以及输出格式。
 
logging.basicConfig([**kwargs])* 这个函数用来配置root logger, 为root logger创建一个StreamHandler,设置默认的格式。* 这些函数: logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical() 如果调用的时候发现root logger没有任何   handler, 会自动调用basicConfig添加一个handler* 如果root logger已有handler, 这个函数不做任何事情,使用basicConfig来配置root logger的输出格式和level:
import logging 
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')  

 

三、关于root logger以及logger的父子关系

前面多次提到root logger, 实际上logger实例之间还有父子关系, root logger就是处于最顶层的logger, 它是所有logger的祖先。

 

root logger是默认的logger

如果不创建logger实例, 直接调用logging.debug()、logging.info()logging.warning()、logging.error()、logging.critical()这些函数,那么使用的logger就是 root logger, 它可以自动创建,也是单实例的。


如何得到root logger

通过logging.getLogger()或者logging.getLogger("")得到root logger实例。


默认的level

root logger默认的level是logging.WARNING

 

如何表示父子关系

logger的name的命名方式可以表示logger之间的父子关系. 比如:parent_logger = logging.getLogger('foo')child_logger = logging.getLogger('foo.bar')

 

什么是effective level

logger有一个概念,叫effective level。 如果一个logger没有显示地设置level,那么它就用父亲的level。如果父亲也没有显示地设置level, 就用父亲的父亲的level,以此推....最后到达root logger,一定设置过level。默认为logging.WARNINGchild loggers得到消息后,既把消息分发给它的handler处理,也会传递给所有祖先logger处理

#encoding:utf-8

import logging

# 设置root logger
r = logging.getLogger()
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
r.addHandler(ch)

# 创建一个logger作为父亲
p = logging.getLogger('foo')
p.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(message)s')
ch.setFormatter(formatter)
p.addHandler(ch)

# 创建一个孩子logger
c = logging.getLogger('foo.bar')
c.debug('foo')

运行结果:

2012-08-31 14:00:24,295 - foo
2012-08-31 14:00:24,295 - DEBUG - foo

可见, 孩子logger没有任何handler,所以对消息不做处理。但是它把消息转发给了它的父亲以及root logger。最后输出两条日志。

 

要想每次程序运行时创建一个新文件,可以向basicConfig()的参数filemode传入值“w”。不过,最好不要采用这种方式管理文件的创建,更好的做法是使用一个RotatingFileHandler,它会自动创建新文件,同时保留原来的日志文件。

使用中发现,用RotatingHandler,并不是每次程序运行时创建一个新文件,日志信息依旧追加到原有的日志文件里。当文件大小超过maxBytes设置的大小限制后,重命名备份该文件。

不用basicConfig,想每次程序运行的日志文件都是新的怎么办?

 

参考:

http://kenby.iteye.com/blog/1162698

posted @ 2012-08-31 14:03  congbo  阅读(883)  评论(0编辑  收藏  举报