我看的时候还是I版和J版,这个时候对log的使用是import logging,现在k版oslo库的进入,log改用oslo_log库了,当然了,差别不大。
要使用log其实蛮简单的,在配置文件中配置log_dir或者log_file, 设置log的level就可以了, 但是如果你比较好奇log是怎么运作的,怎么使用log的,你可以看看:
各个openstack的module对于log的使用大同小异的,以nova为例,我们见得第一眼就是:
logging.setup('nova')
感觉很神奇,貌似加上这一句,然后在各个要使用的类中:
LOG = logging.getLogger(__name__)
LOG.xxx(info,debug,warn)等等就可以使用了
作为调试代码,查看openstack运行原理的利器,虽然我们每个人都有最适合紫的开发方法,有的可能用pdb,有的用远程调试,但是打log几乎是大型分布式系统或
多进程多线程的系统不二方式,就我看类没有比打log更广泛的方式。
这就是setup(你可以用pip或者easy_install下载logging):
def setup(product_name, version='unknown'):
"""Setup logging."""
if CONF.log_config_append:
_load_log_config(CONF.log_config_append)
else:
_setup_logging_from_conf(product_name, version) #正常情况下,我们用openstack给出的log,不配置log_config_append
sys.excepthook = _create_logging_excepthook(product_name)
在_setup_logging_from_conf中最重要的是这几句的理解:
log_root = getLogger(None).logger
def getLogger(name='unknown', version='unknown'):
if name not in _loggers:
_loggers[name] = ContextAdapter(logging.getLogger(name), name,version)
return _loggers[name]
此处返回的ContextAdapater中,logging.getLogger指的是logging包中的RootLogger
for handler in log_root.handlers:
log_root.removeHandler(handler)
#不同的进程会得到不同的RootLogger
logpath = _get_log_file_path()
#得到log文件
if logdir:
binary = binary or _get_binary_name()#os.path.basename(inspect.stack()[-1][1])
return '%s.log' % (os.path.join(logdir, binary),)
是怎么办到的呢,归功于inspect.stack,在mark的link中有介绍,难道执行到当前程序行的调用栈,使用[-1][1]取出的最顶层调用文件,
我想你明白了,我们是如何启动nova的服务的呢,执行的是/usr/bin/nova-api或者/usr/bin/nova-xxx,对应的文件为nova-api或者nova-xxx,
所以!我们在log_dir,也就是/var/log/nova下面对应的log文件是nova-api.log等等
这里,我们知道了日志写到哪里去,且root logger默认的是warning的level,因此默认情况下,你改动程序用debug或者info的level是没有输出的.
于是我们解决了一半的问题,另一半的问题在于:
LOG = logging.getLogger(__name__)
同样的回到getLogger函数:
def getLogger(name='unknown', version='unknown'):
if name not in _loggers:
_loggers[name] = ContextAdapter(logging.getLogger(name),
name,
version)
return _loggers[name]
此时name不为None,返回:
if name:
return Logger.manager.getLogger(name)
magic在哪里,我们trace getLogger有 self._fixupParents(rv)
查看fixupParents:
name = alogger.name
i = name.rfind(".")
rv = None
while (i > 0) and not rv:
substr = name[:i]
if substr not in self.loggerDict:
self.loggerDict[substr] = PlaceHolder(alogger)
else:
obj = self.loggerDict[substr]
if isinstance(obj, Logger):
rv = obj
else:
assert isinstance(obj, PlaceHolder)
obj.append(alogger)
i = name.rfind(".", 0, i - 1)
if not rv:
rv = self.root
alogger.parent = rv
根据name,得到顶层的logger,也就是前面的root logger
在不同的.py文件中使用log时,比如nova/conductor/manager.py,那么最终根据nova.conductor.manager.py,最终
log会写入nova-conductor.log中
我们根据这个规则,我们就能根据module name找到log的位置。
当然了,logger我们有file handler,也有别的handler,不受这些file handler handle的log就会写到满足条件的logger中,
关于这一点可以看Logger的callHandlers方法。
最后一点,如何写log的,文件写入到哪一行等,在Logger的findCaller中可以去详细的看。
事实上,我还没听说哪个团队在开发log的部分,当然了每个模块都少不了,值得看:)
mark link:
http://hyry.dip.jp/tech/book/page/python/debug_stack.html
http://pymotw.com/2/inspect/