Boost Log : Filtering revisited

Filtering revisited

我们在前面的章节中已经提到了过滤,但是我们仅仅触及到了表面。现在我们能够向Log records添加attribute并设置sink,我们就可以构建我们需要的任何复杂的过滤。让我们考虑一下这个例子:

BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)

void init()
{
    // Setup the common formatter for all sinks
    logging::formatter fmt = expr::stream
        << std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
        << ": <" << severity << ">\t"
        << expr::if_(expr::has_attr(tag_attr))
           [
               expr::stream << "[" << tag_attr << "] "
           ]
        << expr::smessage;

    // Initialize sinks
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("full.log"));

    sink->set_formatter(fmt);

    logging::core::get()->add_sink(sink);

    sink = boost::make_shared< text_sink >();

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("important.log"));

    sink->set_formatter(fmt);

    sink->set_filter(severity >= warning || (expr::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));

    logging::core::get()->add_sink(sink);

    // Add attributes
    logging::add_common_attributes();
}

完整代码
在这个示例中,我们初始化了两个sinks:一个用于完整的日志文件,另一个仅用于重要的消息。两个sinks都将以相同的log record格式(我们一开始就初始化并保存到了fmt变量)写入文本文件。 formatter类型是一个带formatter调用签名的无类型函数对象;在许多方面,它可以被视为类似于boost:::function或std::function,除了它从不为空以外。也有用于filters的一个类似的函数对象

值得注意的是formatter本身在这里包含一个filters。如您所见,该格式包含一个条件:仅当日志记录包含“Tag” attribute时才出现。has_attr谓词检查record是否包含“Tag” attribute value并控制它是否被写入文件中。我们使用attribute的关键字来指定谓词的attribute的名称和类型,但是也可以在has_attr调用处中指定它们。条件格式器可以在这里详细解释。

进一步对两个sinks进行初始化。第一个接收器没有任何filters,这意味着它将把每个log record保存到文件中。我们调用第二个sink上的set_filter,只保存severity不低于warning,或者有“Tag” attribute,并且value为“IMPORTANT_MESSAGE”的log record。正如您所看到的,filter语法非常类似于通常的c++,特别是在使用attribute关键字时。

与formatters一样,也可以使用自定义函数作为filter。在这种情况下,Boost.Phoenix非常有用,因为它的bind实现与attribute占位符兼容。前一个例子可以通过以下方式进行修改:

bool my_filter(logging::value_ref< severity_level, tag::severity > const& level,
               logging::value_ref< std::string, tag::tag_attr > const& tag)
{
    return level >= warning || tag == "IMPORTANT_MESSAGE";
}

void init()
{
    // ...

    namespace phoenix = boost::phoenix;
    sink->set_filter(phoenix::bind(&my_filter, severity.or_none(), tag_attr.or_none()));

    // ...
}

如您所见,自定义formatter接收封装在value_ref模板中的attribute values。此包装器包含对指定类型的attribute value的可选引用;如果log record包含所需类型的attribute value,则引用是有效的。可以无条件地应用my_filter中使用的关系运算符,因为如果引用无效,它们将自动返回false。剩下的部分将通过bind表达式完成,该表达式将识severity和tag_attr关键字,并在将其传递给my_filter之前提取相应的值。

Note
由于与Boost.Phoenix集成相关的限制(见#7996),当attribute的关键字与phoenix::bind或phoenix::function一起使用时,如果attribute value缺失,则需要显式指定回退策略。在上面的示例中,这是通过调用or_none完成的,如果没有找到值,这会导致出现一个空的value_ref。在其他情况下,此策略是默认的。还有其他策略可以替代。

可以通过编译和运行测试来尝试这是如何工作的。

猜你喜欢

转载自www.cnblogs.com/kohlrabi/p/9160384.html