PythonLogging处理日志

Logging库被设计成模块的方式,它提供了以下几个子模块:

  • loggers
  • handlers
  • filters
  • formatters.

Loggers 把应用需要直接调用的接口暴露出来。

Handlers 把log记录发到相应的目的地。

Filters 决定哪些记录需要发给handler。

Formatters 定义了log记录的输出格式。

Loggers

Logger对象扮演了三重角色.

  1. 它暴露给应用几个方法以便应用可以在运行时写log。

  2. Logger对象按照log信息的严重程度或者根据filter对象来决定如何处理log信息(默认的过滤功能).

  3. logger还负责把log信息传送给相关的loghandlers.

Logger中最长使用的方法分成两部分中:configuration和message sending.

用于Configuration的方法:

  • setLevel(level)
  • addFilter(filter)
  • removeFilter(filter)
  • addHandler(handler)
  • removeHandler(handler)

setLevel()方法定义了一个logger处理的最底严重程度(比如说中/高/底三种,我定义为中,那么只有严重程度为中或者高的log才会被处理).

debug级别是内置的最低级别,critical是最高级别.

举例来说,如果严重级别设为info级,logger仅仅处理info,warning,error和critical级的log,而debug级别的则忽略掉.

根据logger对象的设置,以下的方法被用来写log:

  • debug(log_message, [args[, *kwargs]])
  • info(log_message, [args[, *kwargs]])
  • warning(log_message, [args[, *kwargs]])
  • error(log_message, [args[, *kwargs]])
  • critical(log_message, [args[, *kwargs]])
  • exception(message[, *args])
  • log(log_level, log_message, [args[, *kwargs]])

debug(),info(),warning(),error()和critical()方法用一个log_message格式字符串和与之对应的各个参数来生成log信息.log_message实际上是一个格式字符串,它可以包含诸如%s,%d,%f此类的替换符号.*args是实际要替换%s,%d,%f参数的列表.至于这个**kwargs关键字参数,logging只处理一个关键字exc_info,这个关键字决定是否对异常信息打log.

exception()跟error()方法基本一样.不同之处是exception()多出一个stack trace用于转储.exception()方法只能从一个exception handler里面调用.

Log()方法显式的带一个level参数,用这个可以得到比使用上面所列举的方法更为详细的log信息,这属于自定义log信息的范畴了.

logging.getLogger([name])方法返回一个logger实例的引用,如果name参数给出,则用这个参数的值作为名字,如果没有则用root做默认值.名字是以点号分割的命名方式命名的(a.b.c).对同一个名字的多个调用logging.getLogger()方法会返回同一个logger对象.这种命名方式里面,后面的loggers是前面logger的子.比如说,有一个名字是foo的logger,那么诸如foo.bar,foo.bar.baz和foo.bam这样的logger都是foo这个logger的子,子loggers自动继承父loggers的log信息,正因为此,没有必要把一个应用的所有logger都配置一边,只要把顶层的logger配置好了,然后子logger根据需要继承就行了.

Handlers

Handler对象负责分配合适的log信息(基于log信息的严重程度)到handler指定的目的地.

logger对象可以用addHandler()方法添加零个或多个handler对象到它自身.

一个常见的场景是,一个应用可能希望把所有的log信息都发送到一个log文件中去,所有的error级别以上的log信息都发送到stdout,所有critical的log信息通过email发送.这个场景里要求三个不同handler处理,每个handler负责把特定的log信息发送到特定的地方.

标准库里面包括以下的handlers:

  • StreamHandler
  • FileHandler
  • RotatingFileHandler
  • TimedRotatingFileHandler
  • SocketHandler
  • DatagramHandler
  • SysLogHandler
  • NTEventLogHandler
  • SMTPHandler
  • MemoryHandler
  • HTTPHandler

本文里面只用到了StreamHandler和FileHandler

Handler里面提供给应用开发者的只有很少的几个方法可用.对使用内置的handler(就是说不是自定义的handlers)的开发者可用的配置方法如下:

  • setLevel(level)
  • setFormatter(formatter)
  • addFilter(filter)
  • removeFilter(filter)

setLevel()方法跟logger对象里面的setLevel()一样,也是用于设定一个最低分发log信息的级别.为什么有两个setLevel()呢?logger的严重等级用于决定那个级别的log信息可以分发到它的handlers.handler里面的level设置用于控制那些个log信息是handler需要转寄的.

setFormatter()方法选定一个格式化对象给它自己用.

addFilter()removeFilter()分别用于为handler增加一个filter和删除一个filter.

应用里面Handler不应该被直接实例化.相反,应该用logging.Handler类作所有Handlers的基类,在它里面定义所有自Handler用到的接口并且创建一些默认的方法让子类来用(或者继承).

示例:

#!/usr/bin/python
import logging
LOG_FILENAME="C:\Python25\log_test.txt"
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
logging.debug("This message should go to the log file")

示例:

#!/usr/bin/python
import logging
logger=logging.getLogger()
handler=logging.FileHandler("Log_test.txt")
logger.addHandler(handler)
logger.setLevel(logging.NOTSET)
logger.error("This is an error message")
logger.info("This is an info message")
logger.critical("This is a critical message")
#执行结果
This is an error message
This is an info message
This is a critical message

说明:

  • 第3行是生成一个日志对象,里面的参数时日志的名字,可以带,也可以不带。
  • 第4行是生成了一个handler,logging支持很多种Handler,像FileHandler,SocketHandler等待,这里由于我们要写文件,所以用了FileHandler,它的参数就是filename,默认当前路径,当然我们可以自己指定路径。
  • 第6行设置日志信息输出的级别。Logging提供了多种日志级别,如NOTSET,DEBUG,INFO,WARNING,ERROR,CRITICAL等,每个级别都对应一个数值,如果我们不自己设置输出级别,那么系统会执行缺省级别,值为30,就warning。Logging也提供了一个方法来查看缺省日志级别,getLevelName(logger,getEffectiveLevel())。

一、日志输出到控制台,写入日志文件

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.DEBUG)

# 定义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.info('foorbar')  

运行后, 在控制台和日志文件都有一条日志:

2011-08-31 19:18:29,816 - mylogger - INFO - foorbar 

二、logging模块的API

logging.getLogger([name])

返回一个logger实例,如果没有指定name,返回root logger。

只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对应的。

这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到 同一个logger实例

Logger.setLevel()

设置logger的level, level有以下几个级别:

NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL

如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出

logger.debug("foobar")    # 不输出 
logger.info("foobar")        # 输出
logger.warning("foobar") # 输出
logger.error("foobar")      # 输出
logger.critical("foobar")    # 输出

Logger.addHandler()
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.WARNING
child loggers得到消息后,既把消息分发给它的handler处理,也会传递给所有祖先logger处理,

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')  

#输出如下:
2011-08-31 21:04:29,893 - foo
2011-08-31 21:04:29,893 - DEBUG - foo

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

来源:
http://crazier9527.iteye.com/blog/290018
http://blog.csdn.net/fxjtoday/article/details/6307285
http://bbs.chinaunix.net/thread-3590256-1-1.html