再谈过滤
我们已经在前面的章节接触过滤,但只是浅尝辄止。我们现在能够添加日志记录并设置接收器的属性,我们需要建立复杂的过滤功能。让我们看下这个例子:
#include <string>
#include <fstream>
#include <iomanip>
#include <boost/log/core.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
enum severity_level
{
normal,
notification,
warning,
error,
critical
};
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
void init()
{
boost::log::formatter fmt = boost::log::expressions::stream
<< std::setw(6) << std::setfill(‘0‘) << line_id << std::setfill(‘ ‘)
<< ": <" << severity << ">\t"
<< boost::log::expressions::if_(boost::log::expressions::has_attr(tag_attr))
[
boost::log::expressions::stream << "[" << tag_attr << "] "
]
<< boost::log::expressions::smessage;
// Initialize sinks
typedef boost::log::sinks::synchronous_sink< boost::log::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);
boost::log::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 || (boost::log::expressions::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));
boost::log::core::get()->add_sink(sink);
// Add attributes
boost::log::add_common_attributes();
}
void logging_function()
{
boost::log::sources::severity_logger< severity_level > slg;
BOOST_LOG_SEV(slg, normal) << "A regular message";
BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
{
BOOST_LOG_SCOPED_THREAD_TAG("Tag", "IMPORTANT_MESSAGE");
BOOST_LOG_SEV(slg, normal) << "An important message";
}
}
int main(int, char*[])
{
init();
logging_function();
return 0;
}
在此示例中,我们创建了两个接收器。一个用于接收完整的日志文件,另一个则会过滤掉一些记录。两个接收器都使用了同一个格式化器fmt,所以它们都会保存相同格式的日志记录。格式化器的类型是一个类型擦除的函数对象,它在许多方面类似于boost::function 或 std::function ,但它从来不会为空。过滤器是有类型的函数对象。
值得注意的是,格式化器本身就包含一个过滤器。正如你所看到的,格式化器的表达式中包含一个判断条件,这是目前唯一在日志记录包含了"Tag"属性。 has_attr 函数检查记录中是否包含"Tag"属性值并控制是否把它置入该文件。
我们进一步讨论两个接收器。第一接收器没有设置任何过滤器,这意味着它将把每个日志记录都保存到文件中。第二接收器调用了set_filter 函数,只保存日志严重级别不超过"warning",并且有一个属性"Tag",属性值必须为"IMPORTANT_MESSAGE"。过滤器的语法非常类似于C++,尤其是使用属性关键字的时候。
如格式化器一样,过滤器也可以使用自定义函数。Boost.Phoenix在这种情况下非常有用,它的绑定实现可以与属性的占位符兼容。下面是修改后的例子:
bool my_filter(boost::log::value_ref< severity_level, tag::severity > const& level,
boost::log::value_ref< std::string, tag::tag_attr > const& tag)
{
return level >= warning || tag == "IMPORTANT_MESSAGE";
}
void init()
{
// ...
sink->set_filter(boost::phoenix::bind(&my_filter, severity.or_none(), tag_attr.or_none()));
// ...
}
正如你可以看到,自定义格式化器接收属性值并纳入 value_ref 模板。如果日志记录包含所需类型的属性值,那么引用是有效的。在 my_filter 里面的关系操作符可以无条件地使用,因为如果引用无效它们会自动返回false。剩下的就是在绑定表达式的时候将severity和tag_attr的值传递给my_filter函数。
时间: 2024-10-08 15:47:41