在 boost::log::sources::severity_logger 自定义接收器后端消耗 () 处理程序中获取严重性的属性值

Posted

技术标签:

【中文标题】在 boost::log::sources::severity_logger 自定义接收器后端消耗 () 处理程序中获取严重性的属性值【英文标题】:Get attribute value for severity in boost::log::sources::severity_logger custom sink backend consume() handler 【发布时间】:2021-12-05 13:45:09 【问题描述】:

想象一下,您想将 Boost 的 boost::log::sources::severity_logger 与一个接受 std::string 消息和 unsigned int 严重级别的端点一起使用。类似于 Win32 API 中 OutputDebugString() 的拉皮条版本。 boost::log 非常方便,因为它内置了过滤、它支持的流插入运算符以及订阅多个后端接收器的可能性。

所以我想出了这个简单的同步自定义接收器后端,其中日志消息在 consume() 函数中提取。我什至能够检查 BOOST_LOG_SEV() 宏设置的严重性属性是否存在。但是无论我尝试什么,我都无法提取将其作为无符号整数传递给 SomeLogChannel() 处理程序的具体值。当然,我错过了一些非常明显的东西。

感谢您的所有提示,请在下面找到源代码。

#include <string>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/basic_sink_backend.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>

namespace logging = boost::log;
namespace sinks = boost::log::sinks;

void SomeLogChannel(unsigned int severity, const std::string& message) 

class SinkBackend : public sinks::basic_sink_backend<sinks::synchronized_feeding>

public:
    explicit SinkBackend() 
    void consume(logging::record_view const& rec)
    
        unsigned int sev;
        std::string message = *rec[boost::log::expressions::smessage];
        if (rec.attribute_values().count("Severity"))
        
            // How to get the value of the (built in) severity attribute?
            // severity = rec.attribute_values()["Severity"].extract<unsigned int>(); // cannot convert from 'boost::log::v2s_mt_nt6::value_ref<T,TagT>' to 'int' with  T=int, TagT=void
            // sev = rec["Severity"].extract<unsigned int>().get(); // Throws exception.
        
        SomeLogChannel(sev, message);
    
;

void init_logging()

    typedef sinks::synchronous_sink<SinkBackend> sink_t;
    boost::shared_ptr<logging::core> core = logging::core::get();
    boost::shared_ptr<SinkBackend> backend(new SinkBackend());
    boost::shared_ptr<sink_t> sink(new sink_t(backend));
    core->add_sink(sink);
    core->set_filter(logging::trivial::severity >= logging::trivial::error);


int main(int, char* [])

    init_logging();
    boost::log::sources::severity_logger< logging::trivial::severity_level > lg;
    BOOST_LOG_SEV(lg, logging::trivial::trace) << "Trace message";
    BOOST_LOG_SEV(lg, logging::trivial::error) << "Error message";
    return 0;

根据 Andrey Semashev 的出色建议,我想出了以下代码:

#include <string>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/basic_sink_backend.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>

void SomeLogChannel(boost::log::trivial::severity_level severity, const std::string& message) 

enum class tVerbosity  eSilent = 0, eBrief, eVerbose ;
BOOST_LOG_ATTRIBUTE_KEYWORD(attr_verbosity, "Verbosity", tVerbosity)

class SinkBackend : public boost::log::sinks::basic_sink_backend<boost::log::sinks::synchronized_feeding>

public:
    void consume(boost::log::record_view const& rec)
    
        boost::log::trivial::severity_level sev = boost::log::trivial::info;
        tVerbosity verbosity = tVerbosity::eVerbose;
        std::string message;
        if (rec[boost::log::expressions::smessage]) message = *rec[boost::log::expressions::smessage];
        if (rec[boost::log::trivial::severity]) sev = *rec[boost::log::trivial::severity];
        if (rec[attr_verbosity]) verbosity = *rec[attr_verbosity]; // Just for debug purposes.
        SomeLogChannel(sev, message);
    
;

void init_logging()

    typedef boost::log::sinks::synchronous_sink<SinkBackend> sink_t;
    boost::shared_ptr<boost::log::core> core = boost::log::core::get();
    boost::shared_ptr<SinkBackend> backend(new SinkBackend());
    boost::shared_ptr<sink_t> sink(new sink_t(backend));
    core->add_sink(sink);
    core->set_filter(boost::log::trivial::severity > boost::log::trivial::debug);
    sink->set_filter(attr_verbosity > tVerbosity::eSilent); // Does not seem to work: filters out everything.


#define MY_LOG(lg, severity, verbosity) BOOST_LOG_SEV(lg, severity) << boost::log::add_value("Verbosity", (tVerbosity)(verbosity))

int main(int, char* [])

    init_logging();
    boost::log::sources::severity_logger< boost::log::trivial::severity_level > lg;
    BOOST_LOG_SEV(lg, boost::log::trivial::trace) << "Trace message";
    BOOST_LOG_SEV(lg, boost::log::trivial::error) << "Error message";
    MY_LOG(lg, boost::log::trivial::info, tVerbosity::eBrief) << "BlahBlahBlah";
    return 0;

这就像一个魅力。除了我努力过滤自定义属性(“详细程度”)之外,不幸的是,这似乎过滤掉了 all 消息。值本身到达 consume() 函数,但是一旦我应用过滤器,所有消息都消失了。看起来很简单,其实很难…… ;-)

也许安德烈也能解决这个难题?再次感谢!

【问题讨论】:

【参考方案1】:

为了从日志记录中提取属性值,您不仅要匹配属性名称,还要匹配值类型。这很重要,因为当属性值存储在日志记录中时,有关值类型的编译时信息会丢失,并且提取器必须知道类型才能以明确定义的方式访问值。

在您的代码中,严重性属性值的类型为logging::trivial::severity_level,而不是unsigned int,正如您在severity_logger 模板中用作严重性级别的类型所证明的那样。因此,在提取此属性值时,您应该指定该类型:

void consume(logging::record_view const& rec)

    unsigned int sev;
    std::string message = *rec[boost::log::expressions::smessage];
    if (logging::value_ref< logging::trivial::severity_level > severity =
        rec["Severity"].extract< logging::trivial::severity_level >())
    
        // Convert the enum to unsigned int
        sev = static_cast< unsigned int >(*severity);
    
    else
    
        // Either "Severity" attribute not found, or it has value type
        // other than logging::trivial::severity_level. Use a default level.
        sev = 0;
    

    SomeLogChannel(sev, message);

但是,为避免重复,最好使用属性关键字。关键字封装了有关属性名称和值类型的信息。方便的是,您已经在过滤器中使用了正确的关键字,因此我们不妨使用它来提取接收器后端中的值:

void consume(logging::record_view const& rec)

    unsigned int sev;
    std::string message = *rec[boost::log::expressions::smessage];
    if (auto severity = rec[logging::trivial::severity])
    
        // Convert the enum to unsigned int
        sev = static_cast< unsigned int >(*severity);
    
    else
    
        // Either "Severity" attribute not found, or it has value type
        // other than logging::trivial::severity_level. Use a default level.
        sev = 0;
    

    SomeLogChannel(sev, message);

顺便说一句,同样的方法适用于提取日志记录消息,您使用smessage 关键字。但是,需要注意的是消息只是另一个属性值,它可能会丢失或具有意外的类型,因此最好检查它是否存在并且可以在使用它之前提取(即在取消引用 @987654328 @从rec[smessage]返回)。


在更新部分,这里的问题是,在您创建的所有三个日志记录中,过滤点的日志记录中都不存在“详细程度”属性。如果未找到参与过滤表达式的属性(或具有意外的值类型),则该表达式返回 false,在您的情况下,这意味着日志记录被抑制。这就是attr_verbosity &gt; tVerbosity::eSilent 过滤器拒绝所有日志记录的原因。

至于为什么缺少该属性,你做的前两条日志记录是通过severity_logger做的,它没有添加这个属性,你也没有添加它。在第三条日志记录中,您使用add_value 操纵器添加它,但该操纵器仅添加它after filtering is done(即操纵器不起作用,因为该记录已被拒绝)。

【讨论】:

谢谢,@Andrey Semashev。这是极好的帮助,就像一个魅力。我已经相应地更新了我的问题,还添加了一个段落来过滤自定义属性,这看起来很简单,但最终让我过滤了所有日志消息...... @Volker 您应该针对遇到的每个问题提出一个单独的问题。我会第一次让它滑动,但以后请避免这样做。 @Volker 我已经更新了我的答案。 再次感谢。说得通。当然,下次我会提出一个新问题。

以上是关于在 boost::log::sources::severity_logger 自定义接收器后端消耗 () 处理程序中获取严重性的属性值的主要内容,如果未能解决你的问题,请参考以下文章

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效

如何在保存在 Mongoose (ExpressJS) 之前在模型中格式化数据

如何在保存在 Mongoose (ExpressJS) 之前在模型中格式化数据