【问题标题】:Use channel hiearchy of Boost.Log for severity and sink filtering使用 Boost.Log 的通道层次结构进行严重性和接收器过滤
【发布时间】:2016-03-25 14:10:50
【问题描述】:

我研究 Boost.Log 已经有一段时间了,我相信现在是我将代码库从 log4cxx 转换为 Boost.Log 的时候了。我相信 Boost.Log 的设计和实现会显着改善我的代码维护和使用。我知道 Boost.Log 常见问题解答有一个 page that says

对于分层记录器,当前库设计中不需要此功能。它在 log4j 中提供的主要好处之一是确定日志记录将在其中的附加程序(接收器,就这个库而言) 结果。该库通过过滤实现了相同的结果。

我理解概念上的等价性,我不想将 Boost.Log 变成 log4j/log4cxx。相反,我的问题是:如何使用 Boost.Log 来获得我目前从 log4cxx 使用的相同功能?特别是,我想为日志源或通道层次结构中的特定节点设置严重性阈值和接收器。例如,我有由libA.moduleB.componentC.logD 组织的日志记录源,层次结构中的级别由点. 分隔。使用 log4cxx 可以将 libA 的整体阈值设置为 INFO,而更具体的记录器 libA.moduleB 的阈值为 DEBUG。

libA.threshold=INFO
libA.moduleB.threshold=DEBUG

同样,可以将接收器附加到层次结构中的任意节点。

我相信 Boost.Log 也可以提供类似的功能,但我需要有关如何实际实现此功能的帮助/指导。另外,我相信其他想从其他框架过渡到 Boost.Log 的人也会有同样的问题。

衷心感谢你们的cmets。

【问题讨论】:

    标签: c++ logging boost boost-log


    【解决方案1】:

    在 Boost.Log 中,接收器(写入日志文件的对象)和记录器(应用程序发出日志记录的对象)没有直接连接,任何接收器都可以从任何记录器接收日志消息。为了使来自某些记录器的记录仅出现在特定的接收器中,您必须在接收器中安排过滤器,以便为不应该接收它们的接收器抑制不必要的记录并传递给其他接收器。为了区分来自不同记录器的记录,记录器必须为他们所做的每条记录添加不同的属性。通常这是通过channels 实现的 - 记录器将附加一个通道属性,该属性可用于识别过滤器、格式化程序或接收器中的记录器。频道可以是combined 以及其他属性,例如严重性级别。必须注意的是,通道和严重级别是正交的,任何通道都可能有任何级别的记录。在过滤器中分别分析不同属性的值。

    因此,例如,如果您希望将通道 A 的记录写入文件 A.log,并将通道 B 的记录写入文件 B.log,则必须创建两个接收器 - 每个文件一个,并设置它们的过滤器相应地。

    BOOST_LOG_ATTRIBUTE_KEYWORD(a_severity, "Severity", severity_level)
    BOOST_LOG_ATTRIBUTE_KEYWORD(a_channel, "Channel", std::string)
    
    logging::add_file_log(
        keywords::file_name = "A.log",
        keywords::filter = a_channel == "A");
    
    logging::add_file_log(
        keywords::file_name = "B.log",
        keywords::filter = a_channel == "B");
    

    请参阅有关 defining attribute keywordsconvenience setup functions 的文档。现在您可以为每个通道创建记录器,并且日志记录将通过过滤器路由到接收器。

    typedef src::severity_channel_logger< severity_level, std::string > logger_type;
    
    logger_type lg_a(keywords::channel = "A");
    logger_type lg_b(keywords::channel = "B");
    
    BOOST_LOG_SEV(lg_a, info) << "Hello, A.log!";
    BOOST_LOG_SEV(lg_b, info) << "Hello, B.log!";
    

    您可以为单个通道拥有任意数量的记录器 - 来自每个通道的消息将被定向到单个接收器。

    但是,这里有两个问题。首先,图书馆不了解通道的性质,并认为它只是一个不透明的值。它不了解频道层次结构,因此“A”和“A.bb”被认为是不同且不相关的频道。其次,如果您希望将多个通道写入单个文件(如“A”和“A.bb”),则设置上述过滤器可能会很困难。如果您希望不同渠道的严重程度不同,事情会变得更加复杂。

    如果通道层次结构对您来说不是很重要,您可以使用severity threshold filter 简化过滤器配置。使用该过滤器,您可以为每个相应的通道设置最低严重性级别。如果您想在子通道中继承严重性阈值,那么您唯一的方法就是编写自己的过滤器;该库不提供开箱即用的功能。

    有多种方法可以创建过滤器,但归结为编写一个函数,该函数接受来自日志记录的属性值,如果此记录通过过滤器,则返回 true,否则返回 false。也许,最简单的方法是在Tutorial 中显示,参见Boost.Phoenix 中的phoenix::bind 示例。

    bool my_filter(
        logging::value_ref< severity_level, tag::a_severity > const& level,
        logging::value_ref< std::string, tag::a_channel > const& channel,
        channel_hierarchy const& thresholds)
    {
        // See if the log record has the severity level and the channel attributes
        if (!level || !channel)
           return false;
    
        std::string const& chan = channel.get();
    
        // Parse the channel string, look for it in the hierarchy
        // and find out the severity threshold for this channel
        severity_level threshold = thresholds.find(chan);
    
        return level.get() >= threshold;
    }
    

    现在设置接收器将像这样更改以使用您的新过滤器:

    logging::add_file_log(
        keywords::file_name = "A.log",
        keywords::filter = phoenix::bind(&my_filter, a_severity.or_none(), a_channel.or_none(), hierarchy_A));
    
    logging::add_file_log(
        keywords::file_name = "B.log",
        keywords::filter = phoenix::bind(&my_filter, a_severity.or_none(), a_channel.or_none(), hierarchy_B));
    

    这里的hierarchy_Ahierarchy_B 是您的数据结构,用于存储两个日志文件的不同通道的严重性阈值。

    【讨论】:

    • 感谢您的回答。感谢您帮助我开始。通道层次结构对我来说很重要,所以我将不得不考虑额外的复杂性。根据您给我的大纲,我的问题似乎归结为编写数据结构和逻辑以支持阈值和接收器过滤器,子节点继承自父节点。使用 log4j,他们强调他们如何有一个最佳方案来做到这一点。你知道是否有人使用 Boost.Log 库实现了类似的方案吗?
    • 不,我想我不知道有人这样做。但是,为严重性阈值制作数据结构看起来并不难 - 您可以使用许多嵌套的 std::map/std::unordered_map 或 Boost.PropertyTree (boost.org/doc/libs/1_60_0/doc/html/property_tree.html) 来实现它,它已经具有序列化/反序列化功能/来自文本。
    • channel_hierarchy是如何定义的?
    • @AndreySemashev - 有没有办法在 add_file_log 调用中使用 lambda 表达式定义 filter(没有 phoenix::bind)?如果是的话,你能给我们举个例子吗?
    • 过滤器只是一个具有特定签名的函数对象:bool (boost::log::attribute_value_set const&amp;)。该函数对象是用 Boost.Phoenix、C++11 lambda 还是普通函数实现的并不重要。如果它支持该签名,则可以将其设置为过滤器。例如:keywords::filter = [](boost::log::attribute_value_set const&amp; attrs) { return true; }.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    • 1970-01-01
    • 2013-04-25
    • 1970-01-01
    • 2018-01-04
    相关资源
    最近更新 更多