我有一个适用于 Linux 和 Windows 的可执行文件,带有三个 dll/共享库,并且我使用 boost.log 进行日志记录。我希望每个模块都有一个单独的文件日志。我的方法是为每个模块创建一个 severity_channel_logger_mt,使用 channel 名称为每个模块构建一个单例。
class LogSingleton final
{
src::severity_channel_logger_mt<logging::trivial::severity_level, std::string> slg_;
...
protected:
LogSingleton() : slg_(boost::log::keywords::channel = "ModuleOne") {}
public:
src::severity_channel_logger_mt<logging::trivial::severity_level, std::string>& logger() { return slg_; }
...
};
然后每个模块在初始化时创建自己的文件接收器,并设置 channel 过滤器,以便只有该模块的日志消息才能到达此接收器。
logging::add_file_log(logging::keywords::file_name = "module_one.log",
logging::keywords::open_mode = std::ios::app,
->set_filter(logging::trivial::severity >= level
&& expr::attr< std::string >("Channel") == "ModuleOne");
然后我创建了我自己的模块特定宏用于日志记录,它传递给正确的记录器。
#define BLOG(lvl) BOOST_LOG_STREAM_WITH_PARAMS((LogSingleton::instance()->logger()), (::boost::log::keywords::severity = ::boost::log::trivial::lvl))
这样使用:
BLOG(info) << "Hello world";
虽然在 Windows 上日志记录按预期工作,但在 Linux 上,ModuleOne(首先初始化)和 ModuleThree(第三个初始化)的日志文件不会收到任何日志消息。 ModuleTwo 正确记录。除了日志接收器文件名、 channel 名称和单例的类名之外,所有三个模块的日志记录代码是相同的。
我想知道问题是否出在我的宏上,但欢迎任何想法,以及对该方法的评论。
更新
我已将这个问题简化为一个最小的示例,全部在一个可执行文件中。共享库问题是一个误导。问题似乎是,如果我在单例中创建 severity_channel_logger,日志记录将失败。如果我使用本地记录器,则可以进行记录。即使我在单例中创建了一个记录器并且不使用它,它也会阻止本地记录器运行。尽管通过代码进行跟踪,但我不明白为什么会这样。 (平台 = Fedora 21,gcc 4.9.2,boost 1.58)
#include <boost/config.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_feature.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/attributes/current_thread_id.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
namespace logging = boost::log;
namespace expr = boost::log::expressions;
namespace src = boost::log::sources;
namespace fs = boost::filesystem;
class LogSingleton
{
src::severity_channel_logger<logging::trivial::severity_level, std::string> slg_;
static LogSingleton* instance_;
LogSingleton(const LogSingleton&);
LogSingleton& operator=(const LogSingleton&);
protected:
LogSingleton() : slg_(boost::log::keywords::channel = "Core") {}
public:
src::severity_channel_logger<logging::trivial::severity_level, std::string>& logger()
{
return slg_;
}
static LogSingleton* instance()
{
if (!instance_)
{
instance_ = new LogSingleton;
}
return instance_;
}
};
LogSingleton* LogSingleton::instance_ = nullptr;
// 1. doesn't work
//#define BLOG(lvl) BOOST_LOG_STREAM_WITH_PARAMS((LogSingleton::instance()->logger()), (::boost::log::keywords::severity = ::boost::log::trivial::lvl))
// access to logger via reference. Works if it is passed a ref to logger declared in main. Doesn't work if it is ref to logger in singleton
#define BLOG(lvl) BOOST_LOG_STREAM_WITH_PARAMS((rlogger), (::boost::log::keywords::severity = ::boost::log::trivial::lvl))
int main(int argc, char **argv)
{
logging::add_common_attributes();
logging::trivial::severity_level level = logging::trivial::trace;
auto formatter = expr::stream
<< "[" << expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%dT%H:%M:%S.%f")
<< "] (" << logging::trivial::severity
<< "): " << expr::message;
fs::path plog = fs::system_complete(fs::path(".."));
if (!fs::exists(plog) || !fs::is_directory(plog))
throw std::invalid_argument("Log directory doesn't exist, or path isn't a directory");
plog /= "core.log";
logging::add_file_log(logging::keywords::file_name = plog.string(),
logging::keywords::open_mode = std::ios::app,
logging::keywords::format = formatter)
->set_filter(logging::trivial::severity >= level && expr::attr< std::string >("Channel") == "Core");
// this works with rlogger macro variant
src::severity_channel_logger<logging::trivial::severity_level, std::string> logger(boost::log::keywords::channel = "Core");
auto& rlogger = logger;
// 2. this doesn't work, with same macro
//auto& rlogger = LogSingleton::instance()->logger();
// 3. just creating the singleton, before or after the creation of the local logger, stops logging from working
//LogSingleton::instance();
BLOG(info) << "Hello world";
return 0;
}
正如目前所写的那样,这个例子适用于本地记录器。标记为 1 的注释 - 这是直接使用单例记录器(失败)的宏的变体。同时注释掉本地记录器并启用对单例的引用将证明该问题。 (评论 2)。标记为 3 的评论表明,仅创建单例记录器会导致通过本地记录器进行记录失败。
最佳答案
问题是由于您的代码中的 LogSingleton
泄漏引起的。单例包含一个记录器,它可以防止记录核心和接收器被破坏。您制作的日志记录已正确处理并写入文件流但未刷新(即它最终在文件流缓冲区中)。通常,流缓冲区在流被销毁时被刷新(在 Boost.Log 的情况下,这发生在接收器被销毁时,在程序终止时)或在每个日志记录之后,如果启用自动刷新(传递 keywords::
参数。add_file_log
函数调用的 auto_flush = true
如果将 LogSingleton::instance_
更改为 std::unique_ptr
,则可以修复它。
关于c++ - 在 Linux 上无法按预期工作的共享库中的增强日志记录 channel 过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31657805/