我有一个日志设置,其中有两种类型的日志消息:
这些属性定义如下:
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
我想创建一个过滤器功能,该功能允许根据两个条件之一将一条消息添加到我的日志中(请注意,基于自定义标签属性的日志消息始终会打印严重性级别信息,具体取决于平凡的记录器的严重性级别。
因此,我希望有一个过滤器,该过滤器根据邮件的严重性来确定邮件是否具有自定义标签,如果没有自定义标签,则允许该邮件。
我试图有一个相对简单的过滤器,该过滤器可以执行以下操作:
sink_->set_filter(
trivial::severity >= severityLevel
|| (expr::has_attr(tag_attr) && tag_attr == "JSON" && logJson_)
);
但是,由于严重性级别可能是Debug,Info,Warning,Error或Fatal,因此如果将级别配置为Debug或Info,则过滤器会忽略自定义标签属性。
我尝试使用c ++ 11 lambda,如下所示:
sink_->set_filter([this, severityLevel](const auto& attr_set) {
if (<condition for custom tag first>) {
return true;
} else if (<condition for severity level second>) {
return true;
} else {
return false;
}
});
但是后来我对如何实际检查我的状况一无所知。我尝试了以下方法:
if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
return true;
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
return true;
} else {
return false;
}
但是编译器会对此抛出一些错误:
Core/Source/Log/Logger.cpp: In lambda function:
Core/Source/Log/Logger.cpp:127:48: error: expected primary-expression before '>' token
if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
^
Core/Source/Log/Logger.cpp:127:50: error: expected primary-expression before ')' token
if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
^
Core/Source/Log/Logger.cpp:129:72: error: expected primary-expression before '>' token
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
^
Core/Source/Log/Logger.cpp:129:74: error: expected primary-expression before ')' token
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
^
Core/Source/Log/Logger.cpp: In lambda function:
Core/Source/Log/Logger.cpp:134:5: error: control reaches end of non-void function [-Werror=return-type]
});
^
cc1plus: all warnings being treated as errors
scons: *** [obj/release/Core/Source/Log/Logger.os] Error 1
====5 errors, 0 warnings====
我一直在寻找有关自己提取属性的增强日志文档,但是找不到所需的信息。
编辑:
为了后代,我将添加解决问题的方式(感谢安德烈的给出的答案):
sink_->set_filter([this, severityLevel](const auto& attr_set) {
if (attr_set[tag_attr] == "JSON") {
return logJson_;
} else if (attr_set[severity] >= severityLevel) {
return true;
} else {
return false;
}
});
答案 0 :(得分:2)
过滤器可以用多种方式编写,我将演示一些替代方法。
首先,使用表达式模板,您可以这样编写:
sink_->set_filter(
(expr::has_attr(tag_attr) && tag_attr == "JSON" && logJson_) ||
trivial::severity >= severityLevel
);
按照C ++的常规短路规则,将首先测试tag属性,如果该条件成功,将不测试严重性。如果标签不存在或JSON不正确或logJson_
不正确,则测试严重性级别。
请注意,上述过滤器将在构造时保存其参数的副本(包括logJson_
和severityLevel
),因此,如果以后更改logJson_
,过滤器将继续使用旧的价值。这与您以后尝试使用C ++ 14 lambdas(通过捕获的logJson_
指针访问this
)的重要区别。如果您实际上想在过滤器中保存对成员logJson_
的引用,则可以使用phoenix::ref
:
sink_->set_filter(
(expr::has_attr(tag_attr) && tag_attr == "JSON" && boost::phoenix::ref(logJson_)) ||
trivial::severity >= severityLevel
);
但是,您应该记住,可以在多个线程中同时调用过滤器,因此对logJson_
的访问不受保护。如果要在运行时更新logJson_
,则必须实现自己的线程同步。
除非存在多线程问题,您第二次尝试使用lambda几乎是正确的。编译器在抱怨,因为lambda函数是模板,并且attr_set["Tag"]
表达式的结果取决于模板参数之一(即attr_set
的类型)。在这种情况下,程序员必须确定以下extract<std::string>()
表达式是模板实例化而不是比较序列。这是通过添加template
关键字来完成的:
if (attr_set["Tag"].template extract<std::string>() == "JSON" && logJson_) {
return true;
} else if (attr_set["Severity"].template extract<trivial::severity_level>() >= severityLevel) {
return true;
} else {
return false;
}
请注意,您可以使用独立功能达到相同的效果,而无需模板限定:
if (boost::log::extract<std::string>("Tag", attr_set) == "JSON" && logJson_) {
return true;
} else if (boost::log::extract<trivial::severity_level>("Severity", attr_set) >= severityLevel) {
return true;
} else {
return false;
}
最后,提取属性值的首选方法是利用先前声明的属性关键字。这样不仅可以避免模板资格的怪癖,而且还消除了很多代码重复。
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
if (attr_set[tag_attr] == "JSON" && logJson_) {
return true;
} else if (attr_set[severity] >= severityLevel) {
return true;
} else {
return false;
}
在这种情况下,从关键字声明中推断出属性值名称和类型。 this部分的末尾记录了这种属性关键字的用法。