python 中得logging系统 - Go语言中文社区

python 中得logging系统


1. logging模块中基本的几个类

logging模块中类继承关系 

图中是涉及到的类的继承关系,下面挑一些关键类进行讲解。

LogRecord

    这是一个记录日志信息的类,构造这样的一个对象,依据其__init__(self, name, level, pathname, lineno,msg, args, exc_info, func=None)需要传入的参数依次是生成该条日志logger的名字,该条日志的等级,输出该日志语句所在文件的路径,输出该日志语句所在的行号,日志字符串,参数,异常信息以及输出该日志语句所在的函数!其中args是tuple形式,元素可以是基本类型,比如int,float,string,也可以是dict。举个例子,msg = "a %(a)d b %(b)s",args = ({'a':1, 'b':2})。为了方便应用(构造Formatter以及Filter),介绍一下该类中的几个成员变量:

变量名

描述

构造Formatter串对应项

self.name

生成该日志logger的名字

%(name)s

self.levelno

日志等级的数值表示

%(levelno)s

self.levelname

日志等级的字符串表示

%(levelname)s

self.pathname

输出该日志其语句所在文件的路径

%(pathname)s

self.filename

self.pathname中得文件名

%(filename)s

self.module

输出该日志其语句所在的模块名

%(module)s

self.lineno

输出该日志语句其语句所在的行号

%(lineno)d 

self.funcName

输出该日志其语句所在的函数

%(funcName)s

self.created

日志被创建的时间(Epoch时间,time.time()的返回值)

%(created)f

self.msecs

self.created的小数部位乘以1000(毫秒单位)

%(msecs)d

self.relativeCreated

日志被创建时间与日志模块被加载时间的时间差(毫秒单位)

%(relativeCreated)d

self.thread

创建该日志线程的ID

%(thread)d

self.threadName

创建该日志线程的名字

%(threadName)s

self.process

创建该日志进程的ID

%(process)d

self.processName

创建该日志进程的名字

%(processName)s(文档里并没有给出,似乎是由于logging不支持多进程访问一个文件【3】,意味着没意义?)

    还有另外两个不应该直接输出的变量(是不应该,不是不能),一个是self.msg, 一个是self.args. 前者直接等于构造对象时传入的参数msg,后者则对传入的参数进行了点改造:如果是基本元素的tuple,直接赋值args;如果是dict的tuple,相应的dict赋值给args。 LogRecord类的getMessage函数主要就是整合这两个变量,返回一个字符串。你可以构造Formatter字符串时使用%(message)s 输出这个字符串。

    可以使用str(LogRecord对象)查看具体的信息,具体输出像这样:

    '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,self.pathname, self.lineno, self.msg)

Formatter

其初始化函数为 __init__(self, fmt=None, datefmt=None),其中fmt为包含LogRecord中提到的“构造Formatter串对应项”的日志格式串,例如,“%(name)s in the thread %(threadName)s”。其默认值为”%s(message)\n“。datefmt 为一个时间格式串,具体的构造方式与构造time.strftime的时间格式串相同。对于日志格式串除去上面提到的,还有一个: %(asctime)s。如果格式串中包含它的话,如果datefmt为None,则%(asctime)s对应被替换为time.strftime("%Y-%m-%d %H:%M:%S", time.localtime (record.created)) + “%03d” % record.msecs;否则,其被替换为time.strftime(datefmt, time.localtime (record.created))。我这里生成一个创建一个简单的Formatter对象,以备后面使用。

expFormatter = Formatter ("%(levelname)s:%(name)s:%(message)s")

这个其实是后面提到的basicConfig( )所应用的一个Formatter。

Filter

这是一个Filter实现的示例性的类,其初始化函数为:__init__(self, name='')。如果不传入任何参数或者name = ‘.’,这个类的filter方法对任何LogRecord都返回True;如果传入诸如“a.b”的name,则这个类的filter方会对任何name中包含‘a.b’的logger生成的LogRecord都返回true,其他的均返回Flase(这里涉及到Logger的层次关系,后面细讲)。

我们可以仿造(继承也没关系,不过我的感觉如果你的Filter和LogRecord名字无关,还是自己再实现一个把!)这个类实现你自己的Filter。构造自己的Filter类,必须要包含一个这样的方法filter(self, record), 其中record为一个LogRecord对象。返回值为布尔类型,具体来说就是可以输出该条日志返回True,不可以输出返回False。举个很简单的例子,实现一个只对id为5的线程生成的LogRecord输出True,其他的均为False

class ThreadFilter(object):

    def filter(self, record):
        if  record.thread == 5:
            return True
        returen  False

 Filterer

这是一个对所有Filter管理的类,而且初始化得到的对象是不包含任何Filter的,具体的成员函数为:

addFilter(self, filter):添加一个filter对象

removeFilter(self, filter):删除一个filter对象

filter(self, record):用所有其管理的filter处理当前日志record

从继承图总我们也可以看到它是Logger和Handler的基类,在这两个类中,都自动调用了filter成员函数,具体来说,都在它们的handle(self, record)中进行了调用。所以只需用addFilter添加和removeFilter删除filter就可以啦,具体使用可以不必关心。举个例子(用上面提到的几个Filter类以及几个没有讲解的类,但相信看了继承图,应该是没有理解难度的):

nameF = Filter(“a.b”)

threadF = ThreadFilter( )

root = RootLogger(WARNING)

hdlr = FileHandler('file1','a')

root.addFilter(threadF)

hdlr.addFilter(nameF)

Handler

这个类主要实现日志的输出,其本身不可以直接使用,必须子类化它并实现一定的函数才可以处理日志,诸如在logging模块中主要有实现了三个类型的Handler,分别是StreamHandler, FileHandler以及NullHandler

(1)几个可能用到的成员函数:

__init__(self, level=NOTSET):初始化一个Handler只需要指定它的日志级别,默认为NOTSET

setLevel(self, level):设置可以处理的最低日志等级

setFormatter(self, fmt):设置使用的Formatter,结合前面提到的一些东西,比如, hdlr.setFormatter(expFormatter), 这样一来,前面例子中得hdlr就使用expFormatter这个Formatter 。若果不做任何设置,其默认使用的Formatter为 Formatter( ), 也即日志格式串为 “%(message)s”,日志中对应显示的时间为time.strftime("%Y-%m-%d %H:%M:%S", time.localtime (record.created)) + “%03d” % record.msecs的样子

(2)其子类中实现的成员函数:

emit(self, record):这个函数必须在子类中实现,否则就会生成NotImplementedError的异常

flush(self):持久或日志,这个在Handler类中啥也没有实现,在子类中要自己设计实现

close(self):这个可以根据不同的handler重写,但重写时一定要调用父类的这个函数

(3)成员变量:

      self.level :Handler的日志级别,如果被处理的日志的级别高于这个值,则处理;否则忽略

      name   :Handler的名字(这是用property加的一个变量)

StreamHandler 

把日志输出到一个流中得Handler,初始化函数为__init__(self, stream=None),如果不指定任何输出流,输出到sys.stderr中

FileHandler

把日志文件输出到一个文件中得Handler,上面曾经给出过一个实际应用的例子

NullHandler

这个Handler什么也不做,一般把他的一个对象添加到一个logger上以避免这样的信息:No handlers could be found for logger XXX

Logger

这个类主要用来生成LogRecord,并转交给它自身的handler处理。

(1)成员变量

        self.name :名字。由初始化函数给定

        self.level:logger的日志级别,默认有初始化函数输入,不给定的话为NOTSET。如果被处理的日志的级别高于这个值,则处理;否则忽略

        self.parent :这个logger的父logger,注意,这个类继承没有任何关系,下面的层次管理详细道来!默认为None 

       self.propagate :决定是否要让它的父logger的handler来处理处理当前日志。默认为1,也即让父logger来处理当前日志

        self.handlers :当前包含的的logger,默认为空

        self.disabled :决定是否可以处理日志,默认为0,也即要处理

        manager:这个变量在logger层次管理中加入的,指向这个logger的管理器(整个log系统原则上来说只有一个manager)

(2)一些常用成员函数

         setLevel(self, level):设定logger的日志级别

         debug(self, msg, *args, **kwargs):生成一个debug级别的日志并处理,例如logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)

         info(self, msg, *args, **kwargs):生成一个info级别的日志并处理

         warning(self, msg, *args, **kwargs):生成一个warning级别的日志并处理,warn(self, msg, *args, **kwargs)也实现相同功能

         error(self, msg, *args, **kwargs):生成一个error级别的日志并处理

         exception(self, msg, *args):生成异常信息并处理,实际上它调用了error函数

         critical(self, msg, *args, **kwargs):生成critical级别的日志并处理,fatal(self, msg, *args, **kwargs)也实现相同功能

         log(self, level, msg, *args, **kwargs):生成一个level级别的日志并处理,例如logger.log(DEBUG, "We have a %s", "mysterious problem", exc_info=1)

        addHandler(self, hdlr):给当前logger添加一个handler hdlr

         removeHandler(self, hdlr):删除掉当前logger中得一个handler hdlr

         getChild(self, suffix):获取当前logger名的一个子孙logger. 例如,当前logger名为“ab”,suffix为“c”, 它将返回一个名为“ab.c”的logger(这和logger的层次架构有关系)

RootLogger

这是为搭建logger的层次架构而继承Logger而构造的一个类,它继承字Logger类,初始化函数为__init__(self, level),其中level为制定的logger级别。实例化这样的一个对象就会生成一个名为root的logger。事实上,模块自己帮我们实例化一个名为“root”,级别为warning的RootLogger。一方面用它来充当层次架构中得根logger;一方面模块级别的函数都使用它处理日志(比如ogging.debug()就使用的它)。

2. 相互的调用关系

为了方便查看源代码,这里给出一个可能得相互调用关系图(以Logger.critical为例):

调用关系

 

3. logging模块级的函数和变量

(1)关于级别的定义,拷贝源码过来:

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

这是log系统预定义的日志级别以及其对应的数值。通过getLevelName(level)对应的字符串名字,也可以通过addLevelName(level, levelName)添加你自己的级别以及级别名。

(2)直接应用以及logger管理,拷贝源码了:

root = RootLogger(WARNING): 在模块定义了根logger
Logger.root = root
Logger.manager = Manager(Logger.root)):定义了一个logger的管理器,从这里也可以看到root是作为一个根logger的

再看看其他的一些函数:

getLogger(name=None):这个函数最常用,要不向manager注册一个名为name的Logger,要不返回root

BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s",为下面basicConfig定义的日志格式串

basicConfig(**kwargs):这个函数主要针对root做配置。如果root已经有handler,它不做任何动作。其默认行为(没有参数)是为root创建并添加一个使用BASIC_FORMAT格式串StreamHandler,其流指向sys.stderr. 可以给定的参数包括:

    filename  指定一个文件,去创建并添加一个 FileHandler 

    filemode  打开文件的方式,默认‘a‘    

    format    Handler要使用的日志格式串

    datefmt  使用的时间格式串.
    level     设置root的日记级别

    stream    用这个流创建并添加一个StreamHandler。如果filename和它都指定,忽略它。

    debug(self, msg, *args, **kwargs):使用root生成一个debug级别的日志并处理,例如loggin.debug("Houston, we have a %s", "thorny problem", exc_info=1)

    info(self, msg, *args, **kwargs):使用root生成一个info级别的日志并处理

    warning(self, msg, *args, **kwargs):使用root生成一个warning级别的日志并处理,warn(self, msg, *args, **kwargs)也实现相同功能

    error(self, msg, *args, **kwargs):使用root生成一个error级别的日志并处理

    exception(self, msg, *args):使用root生成异常信息并处理,实际上它调用了error函数

    critical(self, msg, *args, **kwargs):使用root生成critical级别的日志并处理,fatal(self, msg, *args, **kwargs)也实现相同功能

     log(self, level, msg, *args, **kwargs):使用root生成一个level级别的日志并处理,例如loggingr.log(DEBUG, "We have a %s", "mysterious problem", exc_info=1)

    disable(level):通过manager通知所有的logger,低于level的所有日志都不输出

4. logger的层次架构

通过Manager类,loggin模块实现了一个层次化管理的logger管理系统。比如,运行下面代码:

import logging
logging.getLogger("a.b.c")
logging.getLogger("a.b.d")
logging.getLogger("a.b")

很自然,会生成三个Logger,名字分别为“a.b.c”, “a.b.d” 和“a.b”,但同时还实现了一种树一样的层级关系。root 会有一个字Logger “a.b”, 而Logger “a.b”则会有两个子Logger “a.b.c”和“a.b.d”。这样做的好处就是:

1)可以有选择的输出模块中子模块。以实现soap客户端的suds为例,在实际应用中可以很简单的使用如下语句指定输出 suds.client debug级别以上的日志,而其他模块,比如suds.transport 则不会输出任何日志信息。

import logging
logging.basicConfig( )
logging.getLogger('suds.client').setLevel(logging.DEBUG)

2)方便配置Handler。依赖层级之间的回调关系,可以不必为每个Logger指定Handler,只要其父Logger有指定的Handler就可以。最简单的做法是指指定根 Logger的Handler就可以,上例中使用的就是这种办法

5. Logger的配置及使用

基本的配置可以学学basicConfig函数,拷贝源码了

    _acquireLock()
    try:
        if len(root.handlers) == 0:
            filename = kwargs.get("filename")
            if filename:
                mode = kwargs.get("filemode", 'a')
                hdlr = FileHandler(filename, mode)
            else:
                stream = kwargs.get("stream")
                hdlr = StreamHandler(stream)
            fs = kwargs.get("format", BASIC_FORMAT)
            dfs = kwargs.get("datefmt", None)
            fmt = Formatter(fs, dfs)
            hdlr.setFormatter(fmt)
            root.addHandler(hdlr)
            level = kwargs.get("level")
            if level is not None:
                root.setLevel(level)
    finally:
        _releaseLock()

还有我从别的被人拷贝的[4]

import logging

#create logger
logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)

#create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

#create formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

#add formatter to ch
ch.setFormatter(formatter)

#add ch to logger
logger.addHandler(ch)

#"application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

另外, logging.config模块可以支持从配置读入,相同的实现第二个例子【4】:

配置文件:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

程序如下:

import logging
import logging.config

logging.config.fileConfig("logging.conf") 

#create logger
logger = logging.getLogger("simpleExample")

#"application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

6. logging.handlers模块提供的其他几个类型的handler【2】

BaseRotatingHandler是所有轮徇日志的基类,不能直接使用。但是可以使用RotatingFileHandler和TimeRotatingFileHandler。RotatingFileHandler实例发送信息到磁盘文件,并且限制最大的日志文件大小,并适时轮徇。 TimeRotatingFileHandler实例发送错误信息到磁盘,并在适当的事件间隔进行轮徇。 SocketHandler实例发送日志到TCP/IP socket。 DatagramHandler实例发送错误信息通过UDP协议。 SMTPHandler实例发送错误信息到特定的email地址。 SysLogHandler实例发送日志到UNIX syslog服务,并支持远程syslog服务。 NTEventLogHandler实例发送日志到WindowsNT/2000/XP事件日志。 MemoryHandler实例发送日志到内存中的缓冲区,并在达到特定条件时清空。 HTTPHandler实例发送错误信息到HTTP服务器,通过GET或POST方法。

 

 参考文献:

【1】http://www.python.org/dev/peps/pep-0282/

【2】http://www.cnblogs.com/sislcb/archive/2008/11/25/1340627.html

【3】http://www.cnblogs.com/SophiaTang/archive/2011/09/19/2181471.html

【4】http://crazier9527.iteye.com/blog/290026

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/wujieyhy2006/article/details/7068320
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-27 23:33:20
  • 阅读 ( 1589 )
  • 分类:Go Web框架

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢