使用Boost.Log的通道层次结构进行严重性和接收过滤

时间:2015-12-18 19:46:42

标签: c++ logging boost boost-log

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

  

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

我理解概念等价而不是试图将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的人会有同样的问题。

我真诚地感谢您的评论。

1 个答案:

答案 0 :(得分:10)

在Boost.Log接收器(写入日志文件的对象)和记录器(应用程序通过其发出日志记录的对象)未直接连接,并且任何接收器可能从任何记录器接收日志消息。为了使某些记录器的记录仅出现在特定的接收器中,您必须在接收器中安排过滤器,以便为不应接收它们并为其他接收器传递的接收器抑制不必要的记录。为了区分不同记录器的记录,记录器必须为它们制作的每个记录添加不同的属性。通常,这是通过channels实现的 - 记录器将附加Channel属性,该属性可用于标识过滤器,格式化程序或接收器中的记录器。频道可以是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.Phoenixphoenix::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是您的数据结构,用于存储两个日志文件的不同渠道的严重性阈值。