共享库中的Boost日志通道过滤在linux上没有按预期工作
boost logging channel filtering in shared libraries not working as expected on linux
我有一个Linux和Windows的可执行文件,有三个dll/共享库,我使用boost.log进行日志记录。我希望有一个单独的,文件日志为每个模块。我的方法是为每个模块创建一个severity_channel_logger_mt,在一个单例中为每个模块构造一个通道名。
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_; }
...
};
每个模块在初始化时创建自己的文件接收器,并设置通道过滤器,以便只有该模块的日志消息才能到达该接收器。
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上的日志文件为moduleleone(初始化第一)和ModuleThree(初始化第三)没有收到任何日志消息。module2日志正确。除了日志接收器文件名、通道名和单例的类名之外,所有三个模块的日志代码是相同的。
我想知道问题是否与我的宏,但欢迎任何想法,以及对方法的评论。
我把这个问题简化为一个最小的例子,都在一个可执行文件中。共享库的问题是一个误导。问题似乎是,如果我在单例中创建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的情况下,当sink被销毁时,在程序终止时发生这种情况)或在每个日志记录之后刷新流缓冲区,如果您启用自动刷新(将keywords::auto_flush = true
参数传递给add_file_log
函数调用)。
如果您将LogSingleton::instance_
更改为std::unique_ptr
,则可以修复它
首先确保您将所有模块与Boost.Log的共享库链接起来,而不是静态的。当在所有平台上的多模块应用程序中使用Boost.Log时,这是一个要求。此外,请确保使用影响Boost.Log的同一组配置宏构建所有模块。
接下来,如果您管理符号可见性,请确保从库中导出类型信息符号。这对于异常类型和属性值类型尤其重要。如果您不熟悉符号可见性,您可能希望从导出所有符号开始,这可以通过构建没有任何-fvisibility*
标志的模块来实现。
如果以上都没有帮助,您将不得不调试您的应用程序。特别是,要确保您设置的过滤器实际上正在传递日志记录。您可能需要暂时删除过滤器,看看是否有帮助。如果这仍然没有帮助,请在调试器中一步一步地遍历代码,以查看日志记录在哪个点被删除。
- 这些是什么样的错误?即使我不在 Linux 上工作,我也遇到了 Linux 错误
- 编译在我的 Mac 上工作,但在集群 (Linux) 上不起作用
- 声明C++具有动态大小的数组类型在 Linux 中工作正常,但不能在 Windows 中工作
- 为什么带lcov的codecov在Travis上不能正常工作,而在我当地的Linux Mint上却不能正常工作
- popen() 如何工作以及如何在 Linux 上实现它到 C++ 代码中
- Linux C++读取 UART 设备无法始终如一地工作
- Makefile在Linux中工作,但在Windows下不起作用,在子目录中找不到文件
- C++代码在 Linux 上按预期工作,但在 Windows 上则不然
- QtCUrl post不再工作(Linux nok...窗口确定)
- C++程序使用 mingw 在 Linux 上交叉编译在 MSy2 中工作,但不能直接在 Windows 中工作
- 将Linux打开,读,写,关闭功能以在Windows上工作
- Linux 中共享库中的单例无法按预期工作
- 为什么我的文件/目录操作在Windows上工作正常,但在Linux上不能
- C++:printf 和 wprintf 不能一起工作 (Linux)
- 在macOSX上编译boost_asio/example/ssl/server.cpp可以在Linux上工作
- 为什么这个c++代码在Linux中可以正常工作,而在Windows中却不能
- 可执行文件无法正常工作 [Linux]
- 无法在 Linux 上进行回车工作
- 哔哔声不工作(linux)
- Qt 5.3 QSystemTrayIcon不正常工作[Linux]