1. 程式人生 > >Boost Log : Filtering revisited

Boost Log : Filtering revisited

eid all 嘗試 控制 PE 模板 back 之前 完成

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。在其他情況下,此策略是默認的。還有其他策略可以替代。

可以通過編譯和運行測試來嘗試這是如何工作的。

Boost Log : Filtering revisited