为带有自定义属性和严重性级别的增强日志接收器设置自定义过滤器

时间:2018-09-06 14:43:24

标签: filtering boost-log

我有一个日志设置,其中有两种类型的日志消息:

  • 1只基于严重性级别
  • 1仅基于自定义标签属性

这些属性定义如下:

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;
    }
});

1 个答案:

答案 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部分的末尾记录了这种属性关键字的用法。