社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
图中是涉及到的类的继承关系,下面挑一些关键类进行讲解。
这是一个记录日志信息的类,构造这样的一个对象,依据其__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)
其初始化函数为 __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实现的示例性的类,其初始化函数为:__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
这是一个对所有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)
这个类主要实现日志的输出,其本身不可以直接使用,必须子类化它并实现一定的函数才可以处理日志,诸如在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加的一个变量)
把日志输出到一个流中得Handler,初始化函数为__init__(self, stream=None),如果不指定任何输出流,输出到sys.stderr中
把日志文件输出到一个文件中得Handler,上面曾经给出过一个实际应用的例子
这个Handler什么也不做,一般把他的一个对象添加到一个logger上以避免这样的信息:No handlers could be found for logger XXX
这个类主要用来生成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的层次架构有关系)
这是为搭建logger的层次架构而继承Logger而构造的一个类,它继承字Logger类,初始化函数为__init__(self, level),其中level为制定的logger级别。实例化这样的一个对象就会生成一个名为root的logger。事实上,模块自己帮我们实例化一个名为“root”,级别为warning的RootLogger。一方面用它来充当层次架构中得根logger;一方面模块级别的函数都使用它处理日志(比如ogging.debug()就使用的它)。
为了方便查看源代码,这里给出一个可能得相互调用关系图(以Logger.critical为例):
(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的所有日志都不输出
通过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就可以,上例中使用的就是这种办法
基本的配置可以学学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")
参考文献:
【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
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!