繁体   English   中英

来自多个包层次结构的 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_mainapp_two_main 都导入lib_module (代码如下)。

这些模块显然不共享相同的包结构,因此如果我使用getLogger(__name__) ,默认情况下来自 lib_module 的日志消息不会传播到 app_one 或 app_two

限制

  • app_oneapp_two都将在同一个 Python 会话中运行,因此我无法全局操作lib_module 中记录器的层次结构。
  • 由于我的代码集成到更大系统中的方式,我无法操作全局根记录器。
  • app_oneapp_two有不同的处理程序。 例如,他们将日志写入不同的文件。

一些想法

  1. 这个答案建议将父记录器传递到库代码的函数中。 我猜这会起作用,但它会破坏我现有的几乎所有代码,而且我对以这种方式传递记录器并不感到兴奋。
  2. 我可以子类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_oneapp_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为您获取它们。 记录器对象文档说记录器不应该被直接实例化,而总是通过模块级函数 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")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM