问题描述
我需要为特定的设置设置日志记录。简而言之,我想在两个不同的“父”模块中处理来自公共库代码的日志记录。
app_one \
app_one_main.py
app_two \
app_two_main.py
lib_package \
lib_module.py
app_one_main 和 app_two_main 都导入 lib_module (下面的代码)。
这些模块显然不共享相同的包结构,因此,如果我使用的是getLogger(__name__)
限制
- app_one 和 app_two 都将在同一Python会话中运行,因此我无法在 lib_module 中全局操作记录器的层次结构>。
- 由于我的代码被集成到更大的系统中,因此我无法操作全局根记录器。
- app_one 和 app_two 具有不同的处理程序。例如,他们将日志写入不同的文件。
一些想法
- This answer建议将父记录器传递给库代码的函数。我猜这会起作用,但是它将破坏几乎所有现有代码,并且我对通过这种方式传递记录器并不感到兴奋。
- 我可以继承
logging.Logger
的子类并覆盖Logger.parent
,以便它可以在其包围范围内找到任何记录器。过去,我已经实现了类似的功能,但似乎有些过分设计,并且会破坏默认日志记录系统的许多功能。
代码
# app_one_main.py
import logging
from lib_package import lib_module
logger = logging.getLogger(__name__)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter("APP ONE: %(message)s"))
logger.addHandler(stream_handler)
def log_app_one():
logger.warning("hello from app_one_main")
lib_module.do_the_thing()
# app_two_main.py
import logging
from lib_package import lib_module
logger = logging.getLogger(__name__)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter("APP TWO: %(message)s"))
logger.addHandler(stream_handler)
def log_app_two():
logger.warning("hello from app_two_main")
lib_module.do_the_thing()
# lib_module.py
import logging
logger = logging.getLogger(__name__)
def do_the_thing():
logger.warning("hello from library code")
所需结果
app_one 和 app_two 最终将在Maya之类的另一个平台上运行,该平台提供单个python会话。这两个模块都将导入到同一会话中。
因此,如果我运行app_one_main.log_app_one()
,我会想要:
APP ONE: hello from app_one_main
APP ONE: hello from library code
还有app_two_main.log_app_two()
:
APP TWO: hello from app_two_main
APP TWO: hello from library code
解决方法
主要问题是您要直接实例化Logger
对象,而不是使用getLogger
为您获取它们。 Logger objects文档说,永远不要直接实例化Logger,而应始终通过模块级函数logging.getLogger(name)实例化。当getLogger
创建Logger
时它还会将其插入到日志记录层次结构中,以便其他人可以对其进行配置。您只有自由的浮动Logger
对象。
您的库记录器称为lib_package.lib_module
。移至getLogger
后,任何其他模块都可以获取包记录器lib_package
,对其进行配置,然后其任何子记录器也将起作用。
app_one_main.py
import logging
from lib_package import lib_module
# setup logging output for this module
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter("APP ONE: %(message)s"))
logger = logging.getLogger(__name__)
#logger.setLevel(logging.INFO)
logger.addHandler(stream_handler)
# add handler other modules / packages
pkg_logger = logging.getLogger('lib_package')
pkg_logger.addHandler(stream_handler)
#pkg_logger.setLevel(logging.INFO)
del pkg_logger
logger.warning("hello from app_one_main")
lib_module.do_the_thing()
lib_package / lib_module.py
# lib_module.py
import logging
logger = logging.getLogger(__name__)
def do_the_thing():
logger.warning("hello from library code")
,
这是我登陆的地方。
概述
我正在为所有要使用的工具创建一个通用记录器,并为生活在其上的处理程序创建一个新的Filter子类。过滤器确保仅处理源自与过滤器本身相同的父模块的依赖项的消息。从理论上讲,您可以在根记录器上使用此Filter类和堆栈查找机制,而不是在此“基本记录器”上使用。
代码
custom_logging模块
特殊的调味料在StackFilter
中。它将当前执行堆栈中的模块与实例化时存储的模块进行比较。在某些情况下,这可能行不通,但我还没有找到它们。
import logging
import inspect
# Loggers
BASE_LOGGER_NAME = "_base_logger_"
def get_base_logger():
return logging.getLogger(BASE_LOGGER_NAME)
def get_logger(name):
return logging.getLogger(BASE_LOGGER_NAME + "." + name)
# Filtering
class StackFilter(logging.Filter):
def __init__(self):
self.stack = set(enclosing_modules())
super(StackFilter,self).__init__()
def filter(self,record):
calling_stack = set(enclosing_modules())
return self.stack.issubset(calling_stack)
def enclosing_modules():
frame = inspect.currentframe()
frame_count = 0
_frame = frame
while _frame:
frame_count += 1
_frame = _frame.f_back
mods = [None] * frame_count
i = 0
while frame:
try:
mods[i] = frame.f_globals["__name__"]
except:
pass
i += 1
frame = frame.f_back
return mods
# Logging Handlers
def add_console_output(formatter=None):
base_logger = get_base_logger()
handler = logging.StreamHandler()
if formatter:
handler.setFormatter(formatter)
handler.addFilter(StackFilter())
base_logger.addHandler(handler)
其余代码与原始问题非常相似,但是我添加了一个新模块来检查我的工作。
fake_maya / fake_maya_main.py
这将在今天的Maya中发挥作用,只是将导入和运行我的各种工具的地方。
from app_one import app_one_main
from app_two import app_two_main
app_one_main.log_it()
app_two_main.log_it()
app_one / app_one_main.py
from lib_package import lib_module
import logging
import custom_logging
logger = custom_logging.get_logger(__name__)
formatter = logging.Formatter("APP ONE: %(message)s")
custom_logging.add_console_output(formatter)
def log_it():
logger.warning("hello from app_one_main")
lib_module.do_the_thing()
app_two / app_two_main.py
from lib_package import lib_module
import logging
import custom_logging
logger = custom_logging.get_logger(__name__)
formatter = logging.Formatter("APP TWO: %(message)s")
custom_logging.add_console_output(formatter)
def log_it():
logger.warning("hello from app_two_main")
lib_module.do_the_thing()
lib_package.lib_module.py
import custom_logging
logger = custom_logging.get_logger(__name__)
def do_the_thing():
logger.warning("hello from library code")