在多个模块中使用Python日志logging
我有一个小型的Python项目,具有以下结构 –
Project -- pkg01 -- test01.py -- pkg02 -- test02.py -- logging.conf
我打算使用默认日志logging模块将消息打印到标准输出和日志文件。 要使用日志logging模块,需要进行一些初始化 –
import logging.config logging.config.fileConfig('logging.conf') logr = logging.getLogger('pyApp') logr.info('testing')
目前,我开始logging消息之前,我在每个模块执行此初始化。 是否可以在一个地方只执行一次这样的初始化,以便通过在整个项目中进行日志logging来重复使用相同的设置?
最好的做法是在每个模块中都有一个像这样定义的logging器:
import logging logger = logging.getLogger(__name__)
靠近模块的顶部,然后在模块的其他代码中做例如
logger.debug('My message with %s', 'variable data')
如果您需要细分模块内部的日志logging活动,请使用例如
loggerA = logging.getLogger(__name__ + '.A') loggerB = logging.getLogger(__name__ + '.B')
并根据情况login到loggerA
和loggerB
。
在你的主程序中,例如:
def main(): "your program code" if __name__ == '__main__': import logging.config logging.config.fileConfig('/path/to/logging.conf') main()
要么
def main(): import logging.config logging.config.fileConfig('/path/to/logging.conf') # your program code if __name__ == '__main__': main()
在这里可以看到从多个模块login, 在这里logging代码的configuration,这些代码将被其他代码用作库模块。
更新:当调用fileConfig()
,如果您使用的是Python 2.6或更高版本,则可能需要指定disable_existing_loggers=False
(请参阅文档以获取更多信息)。 为了向后兼容,默认值为True
,这会导致所有现有的logging器被fileConfig()
禁用,除非它们或其祖先在configuration中明确指定。 将值设置为False
,现有的logging器将被保留。 如果使用Python 2.7 / Python 3.2或更高版本,您可能希望考虑比fileConfig()
更好的dictConfig()
API,因为它可以更好地控制configuration。
实际上,每个logging器都是父级包logging器的子级(即package.subpackage.module
从package.subpackage)
inheritanceconfigurationpackage.subpackage)
,所以您只需要configuration根logging器即可。 这可以通过logging.config.fileConfig
(您自己的logging器configuration)或logging.basicConfig
(设置根logging器)来实现。 安装日志logging在你的入口模块( __main__.py
或任何你想运行,例如main_script.py
也适用)
使用basicConfig:
# package/__main__.py import logging import sys logging.basicConfig(stream=sys.stdout, level=logging.INFO)
使用fileConfig:
# package/__main__.py import logging import logging.config logging.config.fileConfig('logging.conf')
然后使用以下命令创build每个logging器:
# package/submodule.py # or # package/subpackage/submodule.py import logging log = logging.getLogger(__name__) log.info("Hello logging!")
有关更多信息,请参阅高级logging教程 。
我总是这样做如下。
使用一个Python文件来configuration我的日志为单身模式,名为“ log_conf.py
”
#-*-coding:utf-8-*- import logging.config def singleton(cls): instances = {} def get_instance(): if cls not in instances: instances[cls] = cls() return instances[cls] return get_instance() @singleton class Logger(): def __init__(self): logging.config.fileConfig('logging.conf') self.logr = logging.getLogger('root')
在其他模块中,只需导入configuration。
from log_conf import Logger Logger.logr.info("Hello")
这是一个简单而有效的单一模式。
扔在另一个解决scheme。
在我的模块的主要初始化我有这样的:
import logging def get_module_logger(mod_name): logger = logging.getLogger(mod_name) handler = logging.StreamHandler() formatter = logging.Formatter( '%(asctime)s %(name)-12s %(levelname)-8s %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.DEBUG) return logger
然后在每个class级我需要一个logging器,我做:
from [modname] import get_module_logger logger = get_module_logger(__name__)
当日志丢失时,您可以通过它们来自的模块区分它们的源。
Yarkee的解决scheme似乎更好。 我想补充一点 –
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances.keys(): cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class LoggerManager(object): __metaclass__ = Singleton _loggers = {} def __init__(self, *args, **kwargs): pass @staticmethod def getLogger(name=None): if not name: logging.basicConfig() return logging.getLogger() elif name not in LoggerManager._loggers.keys(): logging.basicConfig() LoggerManager._loggers[name] = logging.getLogger(str(name)) return LoggerManager._loggers[name] log=LoggerManager().getLogger("Hello") log.setLevel(level=logging.DEBUG)
所以LoggerManager可以是整个应用程序的插件。 希望它是有道理的和有价值的。
你也可以拿出这样的东西!
def get_logger(name=None): default = "__app__" formatter = logging.Formatter('%(levelname)s: %(asctime)s %(funcName)s(%(lineno)d) -- %(message)s', datefmt='%Y-%m-%d %H:%M:%S') log_map = {"__app__": "app.log", "__basic_log__": "file1.log", "__advance_log__": "file2.log"} if name: logger = logging.getLogger(name) else: logger = logging.getLogger(default) fh = logging.FileHandler(log_map[name]) fh.setFormatter(formatter) logger.addHandler(fh) logger.setLevel(logging.DEBUG) return logger
现在,如果在一个单独的模块中定义了上述内容,并且在其他模块中导入了日志logging,则可以在同一模块中使用多个logging器。
a=get_logger("__app___") b=get_logger("__basic_log__") a.info("Starting logging!") b.debug("Debug Mode")
其中几个答案表明,在你做的模块的顶部
import logging logger = logging.getLogger(__name__)
我的理解是这被认为是非常糟糕的做法 。 原因是文件configuration将默认禁用所有现有的logging器。 例如
#my_module import logging logger = logging.getLogger(__name__) def foo(): logger.info('Hi, foo') class Bar(object): def bar(self): logger.info('Hi, bar')
而在你的主要模块中:
#main import logging # load my module - this now configures the logger import my_module # This will now disable the logger in my module by default, [see the docs][1] logging.config.fileConfig('logging.ini') my_module.foo() bar = my_module.Bar() bar.bar()
现在,logging.ini中指定的日志将为空,因为现有的日志logging程序已被fileconfig调用禁用。
虽然是肯定可以解决这个问题(disable_existing_Loggers = False),你的图书馆的许多客户端实际上不会知道这种行为,并将不会收到您的日志。 通过始终在本地调用logging.getLogger,使您的客户变得轻松。 提示:我从Victor Lin的网站了解到这个行为。
所以好的做法是总是在本地调用logging.getLogger。 例如
#my_module import logging logger = logging.getLogger(__name__) def foo(): logging.getLogger(__name__).info('Hi, foo') class Bar(object): def bar(self): logging.getLogger(__name__).info('Hi, bar')
另外,如果在main中使用fileconfig,请设置disable_existing_loggers = False,以防库devise人员使用模块级logging器实例。