昨天 Lord Leatherface 问我为什么使用了 suds 后,输出日志变成了两条。于是我仔细地研究了 logging 的日志处理机制,并且再次考虑了 uliweb 的日志处理,最终对 uliweb的 日志进行了重构,并且弄清楚了为什么会有两条日志的现象,因此以本文作一个记录。
对于 logging 我想大家用得应该不少,那么我先提几个问题:
- logging 直接输出日志,如 logging.info() 与 log = logging.getLogger('name') log.info() 有什么不同?
- “No handlers could be found for logger”是怎么回事?
- root logger 有什么用,如何获得?
- 形如 "a.b.c" 的 logger 名有什么用?
- 如果一个 logger 执行多次 addHandler,那么每个 handler 都会被执行吗?
- logger 的传播(propagate)是怎么回事?如何阻止它?
logging 中 log 是可以分级的,它的级别与你使用 getLogger() 中的名字有关系。比如,你可以使用 "uliweb" 或 "uliweb.app",它们的区别就是 "uliweb.app" 中有一个 '.' 。那么 logging 会自动生成 "uliweb" 和 "uliweb.app" 的 logger,并且 "uliweb" 将是 "uliweb.app" 的 logger 的父对象。这一点可以这样验证:
1
2
3
4
|
>>>
import
logging
>>> log
=
logging.getLogger(
'uliweb.app'
)
>>> log.manager.loggerDict.items()
[(
'uliweb.app'
, <logging.Logger instance at
0x017CE238
>), (
'uliweb'
, <logging.PlaceHolder instance at
0x017CE2D8
>)]
|
再看一看父子关系:
1
2
3
4
5
6
7
|
>>> log.parent
<logging.RootLogger instance at
0x017E3AD0
>
>>> log1
=
logging.getLogger(
'uliweb'
)
>>> log.parent
<logging.Logger instance at
0x01820DA0
>
>>> log.parent.name
'uliweb'
|
1
2
3
|
root
=
RootLogger(WARNING)
Logger.root
=
root
Logger.manager
=
Manager(Logger.root)
|
root 对象有什么用呢?它就是缺省的日志对象。使用 logging 的最简单的方法是:
1
2
|
import
logging
logging.info()
|
1
2
3
4
5
6
7
|
def
info(msg,
*
args,
*
*
kwargs):
"""
Log a message with severity 'INFO' on the root logger.
"""
if
len
(root.handlers)
=
=
0
:
basicConfig()
root.info(
*
((msg,)
+
args),
*
*
kwargs)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def
callHandlers(
self
, record):
c
=
self
found
=
0
while
c:
for
hdlr
in
c.handlers:
found
=
found
+
1
if
record.levelno >
=
hdlr.level:
hdlr.handle(record)
if
not
c.propagate:
c
=
None
#break out
else
:
c
=
c.parent
if
(found
=
=
0
)
and
raiseExceptions
and
not
self
.manager.emittedNoHandlerWarning:
sys.stderr.write(
"No handlers could be found for logger"
" \"%s\"\n"
%
self
.name)
self
.manager.emittedNoHandlerWarning
=
1
|
那么如果我想获得 root 对象该如何做呢?使用 logging.root 吗?常用的方法还是通过 getLogger('')来获得。参数可以是 '' 或其它为假的值。
通过上面的讲解,我想你应该知道我提的几个问题的答案了吧。下面再简单总结一下:
- logger 是分级的,有父子关系;
- logger 的处理要靠 handler 来输出;
- 获得了 logger 并不一定会自动创建 handler,因此 logger 一般都需要配置;
- 在存在父子关系的情况下,日志是可以被传播输出的,并且可以通过 propagate 属性来控制。