c++ - 在 boost::log::sources::severity_logger 自定义接收器后端消耗 () 处理程序中获取严重性的属性值
问题描述
想象一下,您想将 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;
}
这就像一个魅力。除了我努力过滤自定义属性(“详细程度”)之外,不幸的是,这似乎过滤掉了所有消息。值本身到达 consume() 函数,但是一旦我应用过滤器,所有消息都消失了。看起来很简单,但又很难…… ;-)
也许安德烈也能解决这个难题?再次感谢!
解决方案
为了从日志记录中提取属性值,您不仅要匹配属性名称,还要匹配值类型。这很重要,因为当属性值存储在日志记录中时,有关值类型的编译时信息会丢失,并且提取器必须知道类型才能以明确定义的方式访问该值。
在您的代码中,严重性属性值具有 type 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
关键字。但是,需要注意的是消息只是另一个属性值,它可能会丢失或具有意外的类型,因此最好检查它是否存在并且可以在使用它之前提取(即在取消引用value_ref
返回之前) rec[smessage]
)。
在更新的部分,这里的问题是“详细程度”属性在过滤时的日志记录中不存在,在您制作的所有三个日志记录中。如果未找到参与过滤表达式的属性(或具有意外的值类型),则该表达式返回false
,在您的情况下,这意味着日志记录被抑制。这就是attr_verbosity > tVerbosity::eSilent
过滤器拒绝所有日志记录的原因。
至于为什么缺少该属性,您做的前两条日志记录是通过 制作的severity_logger
,它没有添加该属性,您也没有添加它。在第三条日志记录中,您使用add_value
操纵器添加它,但该操纵器仅在过滤完成后才添加它(即操纵器不工作,因为该记录已被拒绝)。
推荐阅读
- python - 使用屏幕管理器时不显示相机图像
- c++ - C++ 使用什么编译器
- swift - 如何在 SwiftUI 中声明“全局 @State 变量”?
- python - 将元素从一个列表附加到另一个列表的基本“for”循环问题
- c# - 碰撞问题。没有自动触发
- javascript - 无法在已部署版本中检索图像,而图像在本地主机上运行的反应应用程序中完美加载?
- python - 预期类型字节并得到 str 而不是 udp 客户端错误
- c++ - 了解 C++ 堆栈对象分配
- java - 按集合中的元素查找
- nginx-reverse-proxy - ESXi vCenter 7.0 NginX 反向代理问题