在 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 > tVerbosity::eSilent
过滤器拒绝所有日志记录的原因。
至于为什么缺少该属性,你做的前两条日志记录是通过severity_logger
做的,它没有添加这个属性,你也没有添加它。在第三条日志记录中,您使用add_value
操纵器添加它,但该操纵器仅添加它after filtering is done(即操纵器不起作用,因为该记录已被拒绝)。
【讨论】:
谢谢,@Andrey Semashev。这是极好的帮助,就像一个魅力。我已经相应地更新了我的问题,还添加了一个段落来过滤自定义属性,这看起来很简单,但最终让我过滤了所有日志消息...... @Volker 您应该针对遇到的每个问题提出一个单独的问题。我会第一次让它滑动,但以后请避免这样做。 @Volker 我已经更新了我的答案。 再次感谢。说得通。当然,下次我会提出一个新问题。以上是关于在 boost::log::sources::severity_logger 自定义接收器后端消耗 () 处理程序中获取严重性的属性值的主要内容,如果未能解决你的问题,请参考以下文章
在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?