Python 在多个模块中使用日志记录
我有一个小型python项目,其结构如下-Python 在多个模块中使用日志记录,python,logging,config,Python,Logging,Config,我有一个小型python项目,其结构如下- Project -- pkg01 -- test01.py -- pkg02 -- test02.py -- logging.conf 我计划使用默认的日志模块将消息打印到标准输出和日志文件。 要使用日志模块,需要进行一些初始化- import logging.config logging.config.fileConfig('logging.conf') logger = logging.getLogger('pyApp')
Project
-- pkg01
-- test01.py
-- pkg02
-- test02.py
-- logging.conf
我计划使用默认的日志模块将消息打印到标准输出和日志文件。
要使用日志模块,需要进行一些初始化-
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')
logger.info('testing')
目前,在开始记录消息之前,我在每个模块中执行此初始化。是否可以在一个位置只执行一次初始化,以便通过在整个项目中记录来重用相同的设置?我始终按照以下方式执行初始化 使用单个python文件将我的日志配置为名为“
log\u 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')
在另一个模块中,只需导入配置
from log_conf import Logger
Logger.logr.info("Hello World")
这是一种简单而高效的单例日志模式。实际上,每个日志记录器都是父级包日志记录器的子级(即
包.subpackage.module
从包.subpackage>继承配置)
,因此只需配置根日志记录器即可。这可以通过(您自己的记录器配置)或(设置根记录器)实现。在输入模块中设置日志记录(\uuuuu main\uuuuuuuuupy
或任何您想要运行的程序,例如main\u script.py
\uuuuuuuu init\uuuuuuuuuupy
也可以运行)
使用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')
然后使用以下方法创建每个记录器:
# package/submodule.py
# or
# package/subpackage/submodule.py
import logging
log = logging.getLogger(__name__)
log.info("Hello logging!")
有关更多信息,请参阅。最佳做法是在每个模块中定义一个记录器,如下所示:
import logging
logger = logging.getLogger(__name__)
靠近模块顶部,然后在模块中的其他代码中执行,例如
logger.debug('My message with %s', 'variable data')
如果需要在模块内细分日志记录活动,请使用
loggerA = logging.getLogger(__name__ + '.A')
loggerB = logging.getLogger(__name__ + '.B')
并根据需要登录到loggerA
和loggerB
在主程序或程序中,执行以下操作:
def main():
"your program code"
if __name__ == '__main__':
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
main()
或
有关多个模块的日志记录,以及其他代码将用作库模块的代码的日志记录配置,请参阅
更新:调用
fileConfig()
时,如果您使用的是Python 2.6或更高版本,则可能需要指定禁用现有的\u loggers=False
(有关更多信息,请参阅)。对于向后兼容性,默认值为True
,这会导致所有现有记录器被fileConfig()
禁用,除非它们或它们的祖先在配置中显式命名。当值设置为False
时,现有的记录器将不受影响。如果使用Python 2.7/Python 3.2或更高版本,您可能希望考虑<代码>独占配置()/<代码> API,它比您也可以提出这样的事情!
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
现在您可以在同一个模块和整个项目中使用多个记录器,前提是上述记录器是在一个单独的模块中定义的,并且在需要日志记录的其他模块中导入
a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")
@亚基的解决方案似乎更好。我想再补充一点-
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可以插入整个应用程序。
希望它有意义和价值。加入另一个解决方案 在我的模块的init.py中,我有如下内容:
# mymodule/__init__.py
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
然后在每个模块中,我需要一个记录器,我需要:
# mymodule/foo.py
from [modname] import get_module_logger
logger = get_module_logger(__name__)
当日志丢失时,您可以根据它们来自的模块来区分它们的来源 其中一些答案表明,在模块的顶部
import logging
logger = logging.getLogger(__name__)
我的理解是,这被认为是非常糟糕的做法。原因是默认情况下,文件配置将禁用所有现有记录器。例如
#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中指定的日志将为空,因为fileconfig调用禁用了现有的记录器
虽然可以绕过这个问题(disable_existing_Loggers=False),但实际上,您库的许多客户机都不知道这个行为,也不会收到您的日志。通过始终在本地调用logging.getLogger,为您的客户提供方便。帽子提示:我是从一个朋友那里了解到这种行为的
因此,最好的做法是始终在本地调用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,以防库设计者使用模块级记录器实例。有几个答案。我最终得到了一个类似但不同的解决方案,这对我来说是有意义的,也许对你来说也是有意义的。 我的主要目标是能够按级别将日志传递给处理程序(将调试级别的日志传递给控制台,将警告和更高级别的日志传递给文件): 创建了一个名为logger.py的漂亮的util文件:
import logging
def get_logger(name):
return logging.getLogger("flask.app." + name)
flask.app是flask中的硬编码值。应用程序记录器始终以flask.app作为其模块名称
现在,在每个模块中,我都可以在以下模式下使用它:
from logger import get_logger
logger = get_logger(__name__)
logger.info("new log")
这将以最小的工作量为“app.flask.MODULE_NAME”创建一个新日志。最佳做法是单独创建一个模块,该模块只有一个方法,我们的任务是为调用方法提供一个记录器处理程序。将此文件另存为m_logger.py
import logger, logging
def getlogger():
# logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
#ch = logging.StreamHandler()
ch = logging.FileHandler(r'log.txt')
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
return logger
现在,只要需要记录器处理程序,就调用getlogger()方法
from m_logger import getlogger
logger = getlogger()
logger.info('My mssg')
python新手,所以我不知道这是否可取,但它非常适合不重新编写样板文件 项目必须具有init.py,才能作为模块加载
#将其放入模块的uuu init.py中
导入logging.config
导入系统
#我用了这个字典测试,你会说:
#logging.config.fileConfig('logging.conf')
#loggers中的“”条目是根记录器,教程始终为
#使用“根”,但我不能让它工作
logging.config.dictConfig({
“版本”:1,
“格式化程序”:{
“默认值”:{
“格式”:“%(asctime)s%(levelname)s%(name)s%(消息)s”
},
},
“处理程序”:{
“控制台”:{
“级别”:“调试”,
“类”:“logging.StreamHandler”,
“流”:ext://sys.stdout"
}
},
“伐木工人”:{
"": {
“级别”:“调试”,
import logger, logging
def getlogger():
# logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
#ch = logging.StreamHandler()
ch = logging.FileHandler(r'log.txt')
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
return logger
from m_logger import getlogger
logger = getlogger()
logger.info('My mssg')
import logging
logger = logging
logger.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
from base_logger import logger
if __name__ == '__main__':
logger.info("This is an info message")