Python 从多个包层次结构中记录(不使用根记录器)
Python logging from multiple package hierarchies (without using root logger)
我需要为一个相当具体的设置设置日志记录。简而言之,我想处理来自两个不同“父”模块中的一段公共库代码的日志记录。
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 文档说 Loggers 不应该直接实例化,但总是通过 module-level 函数 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")
这是我降落的地方。
概览
我正在为我的所有工具创建一个通用记录器,并为它上面的处理程序创建一个新的过滤器子class。过滤器确保只处理源自与过滤器本身相同的父模块的依赖项的消息。理论上,您可以在根记录器上使用此过滤器 class 和堆栈查找机制,而不是此“基本记录器”。
代码
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")
我需要为一个相当具体的设置设置日志记录。简而言之,我想处理来自两个不同“父”模块中的一段公共库代码的日志记录。
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 文档说 Loggers 不应该直接实例化,但总是通过 module-level 函数 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")
这是我降落的地方。
概览
我正在为我的所有工具创建一个通用记录器,并为它上面的处理程序创建一个新的过滤器子class。过滤器确保只处理源自与过滤器本身相同的父模块的依赖项的消息。理论上,您可以在根记录器上使用此过滤器 class 和堆栈查找机制,而不是此“基本记录器”。
代码
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")