Boost.log默认控制台输出模式下如何开启自动刷新?无法获取现有Sink
我完全理解你的需求——想用默认的控制台输出,同时开启自动刷新,但又不想额外添加新的Sink,还发现boost::log::core好像没有直接获取现有Sink的方法?其实有几个可行的思路:
思路1:遍历现有Sink并配置自动刷新
其实boost::log::core是提供了遍历所有已注册Sink的方法的,你可能没注意到get_sinks()这个成员函数。它会返回一个包含所有Sink指针的范围,你可以遍历这个范围,找到对应的控制台Sink,然后设置自动刷新。
示例代码:
#include <boost/log/core.hpp> #include <boost/log/sinks/text_ostream_backend.hpp> #include <boost/log/sinks/sync_frontend.hpp> // ... auto core = boost::log::core::get(); auto sinks = core->get_sinks(); for (auto& sink : sinks) { // 尝试将Sink转换为text_ostream_frontend类型(默认控制台Sink是这个类型) auto text_sink = boost::dynamic_pointer_cast<boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend>>(sink); if (text_sink) { // 获取后端并开启自动刷新 text_sink->locked_backend()->auto_flush(true); break; // 找到控制台Sink后退出循环 } }
这段代码会遍历所有注册的Sink,找到文本输出类型的Sink(也就是默认的控制台Sink),然后开启它的自动刷新功能。默认的控制台Sink确实是synchronous_sink<text_ostream_backend>类型,所以这个转换大概率能成功。
思路2:手动创建并配置控制台Sink(替代默认)
如果你觉得遍历Sink不够直接,也可以手动创建控制台Sink并配置自动刷新,然后替换掉默认的Sink(或者直接不依赖默认创建,自己注册)。这样你能完全控制Sink的行为,代码也更清晰:
#include <boost/log/core.hpp> #include <boost/log/sinks/text_ostream_backend.hpp> #include <boost/log/sinks/sync_frontend.hpp> #include <iostream> // ... // 创建文本输出后端 auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>(); backend->add_stream(boost::shared_ptr<std::ostream>(&std::cout, boost::null_deleter())); backend->auto_flush(true); // 开启自动刷新 // 创建同步前端并绑定后端 auto sink = boost::make_shared<boost::log::sinks::synchronous_sink<decltype(backend)>>(backend); // 获取日志核心并移除所有默认Sink(如果有的话) auto core = boost::log::core::get(); core->remove_all_sinks(); // 注册我们自己的Sink core->add_sink(sink);
这里用boost::null_deleter是因为std::cout是全局对象,不需要我们去销毁它。这种方法的好处是你完全掌控Sink的配置,不用担心默认Sink的行为变化。
思路3:直接操作标准输出流(简单但不够优雅)
如果上面两种方法都觉得麻烦,还有个快速的办法:直接给std::cout设置unitbuf标志,这样每次输出后都会自动刷新缓冲区:
std::cout << std::unitbuf;
不过这个方法是全局的,会影响所有使用std::cout的代码,而不仅仅是Boost.Log的输出。如果你的程序里还有其他地方用std::cout,可能会导致不必要的性能开销,所以更推荐前两种方法。
内容的提问来源于stack exchange,提问作者Andre

