boost::log 将消息保存在容器中,然后将所有消息发送到后端

boost::log save message in container and then send all messages to backend

我想用 memory_sink 之类的东西将实际格式化的消息保存在内存中,然后 flush 在后端发送消息。但是有一个问题,保存的record_view的属性被更改为最后创建的record_view的属性,我不知道为什么会这样。但可能有人会说我,我可以实现我想要的东西吗?

真正最小的例子:

#define BOOST_LOG_DLL 1
#include <boost/log/core.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/basic_sink_backend.hpp>
#include <boost/log/expressions/keyword.hpp>
#include <boost/log/trivial.hpp>
#undef BOOST_LOG_DLL

#include <boost/shared_ptr.hpp>
#include <queue>

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

typedef sinks::combine_requirements<sinks::synchronized_feeding, sinks::flushing>::type sync_flushing;

struct message_t
{
   message_t(const logging::record_view& r, const std::string& f) :
      record(r), fstring(f)
   {
   }
   logging::record_view record;
   std::string fstring;
};

template<typename Sink>
class memory_sink : public sinks::basic_formatted_sink_backend<char, sync_flushing>
{
public:
   memory_sink(const boost::shared_ptr<Sink>& sink) : sink_(sink)
   {
   }

   void consume(const logging::record_view& rec, const string_type& fstring)
   {
      const message_t msg(rec, fstring);
      messages_.push(msg);
   }

   void flush()
   {
      while (!messages_.empty())
      {
         const message_t& msg = messages_.front();
         sink_->consume(msg.record, msg.fstring);
         messages_.pop();
      }
   }
private:
   typedef std::queue<message_t> buffer_t;
   buffer_t messages_;
   const boost::shared_ptr<Sink> sink_;
};

std::ostream& operator << (std::ostream& stream, logging::trivial::severity_level lvl)
{
   return stream << boost::log::trivial::to_string(lvl);
}

class cout_sink : public sinks::basic_formatted_sink_backend< char, sinks::synchronized_feeding >
{
public:
   std::string format(const logging::record_view& rec, const std::string& fstring) const
   {
      return fstring;
   }
   void consume(const logging::record_view& rec, const std::string& fstring)
   {
      std::cout << "[" << rec[boost::log::trivial::severity] << "] " << fstring << std::endl;
   }
};

void init_cout()
{
   typedef sinks::synchronous_sink<memory_sink<cout_sink> > sink_t;

   boost::shared_ptr< logging::core > core = logging::core::get();
   core->remove_all_sinks();

   boost::shared_ptr< cout_sink > tsink = boost::make_shared<cout_sink>();
   boost::shared_ptr<memory_sink<cout_sink> > backend = boost::make_shared<memory_sink<cout_sink> >(tsink);
   boost::shared_ptr< sink_t > sink = boost::make_shared<sink_t>(tsink);

   core->add_sink(sink);
}

void flush_logs()
{
   logging::core::get()->flush();
}

int main()
{
   logging::add_common_attributes();
   init_cout();
   BOOST_LOG_TRIVIAL(warning) << "warning message";
   BOOST_LOG_TRIVIAL(error) << "error message";
   flush_logs();
}

Live version

如您所见,两条记录的严重性均为 error,但我预计 warningerror。可能有更好的方法来做到这一点?

我知道格式化程序,这里严重性打印在 backend 中,仅作为示例,因为实际代码更大并且在消费接收时下沉 record_view,而不是仅将其发送到后端,这也仅接收 record_view.

您观察到的行为是由库使用的优化技术引起的。创建日志记录时,Boost.Log 有时可以通过引用包含原始值的线程特定数据来避免将属性值复制到记录中。在处理记录时,特定于线程的数据应该保持不变。显然需要在线程之间传递记录的异步接收器有一种与日志记录核心通信的方式,因此禁用此优化并且日志记录在传递到接收器之前与原始线程分离。这被描述为 here,请参阅关于 detach_from_thread 的注释。

问题是只有接收器前端决定是否必须禁用此优化,而且只有 asynchronous_sink 会这样做。没有办法为其他前端定制它。所以要么你必须使用你的后端asynchronous_sink,要么你也必须编写你的前端。

要实现接收器前端,您需要一个从 sink 基础 class 派生的 class 并执行过滤、格式化(如果需要)并将记录传递到后端。您可以使用 synchronous_sink 实现作为示例。与 synchronous_sink 的重要区别在于您必须将 true 传递给 sink 构造函数。这将告诉日志记录核心您的接收器需要分离的日志记录。