在共享库中使用boost log severity失效,参数lvl始终为0求排查
问题描述
在项目中使用boost::log并封装到共享库后,链接该库的测试程序出现severity功能失效的问题:日志输出时operator<<函数的lvl参数始终为0。调试Boost源码发现,设置和获取日志级别时,get_severity_level()函数存在不同实例。
共享库项目(log-lib)代码如下:
logger.h
#ifndef LOGGER_H #define LOGGER_H #include <unordered_map> #include <boost/log/common.hpp> #include <boost/log/sources/logger.hpp> #include <boost/log/sources/record_ostream.hpp> #include <boost/log/utility/manipulators/add_value.hpp> #include <boost/log/sinks/sync_frontend.hpp> #include <boost/log/sinks/text_file_backend.hpp> #include <boost/log/sinks/text_ostream_backend.hpp> #if defined(LOG_LIBRARY) #define LOGGER_EXPORT __declspec(dllexport) #else #define LOGGER_EXPORT __declspec(dllimport) #endif enum class LogLevel { LOG_ALL = 0, LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_OFF }; class LOGGER_EXPORT Logger { public: static Logger* Instance(); private: static std::shared_ptr<Logger> m_instance; explicit Logger(); public: boost::log::sources::severity_logger_mt<LogLevel>& GetLogger(); private: boost::log::sources::severity_logger_mt<LogLevel> m_logger; }; #endif //LOGGER_H
logger.cpp
#include <stdexcept> #include <string> #include <iostream> #include <boost/algorithm/string.hpp> #include <boost/smart_ptr/shared_ptr.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> #include <boost/log/expressions.hpp> #include <boost/log/attributes.hpp> #include <boost/log/utility/setup/console.hpp> #include <boost/log/utility/setup/common_attributes.hpp> #include <boost/log/sinks/sync_frontend.hpp> #include <boost/log/sinks/text_file_backend.hpp> #include <boost/log/support/date_time.hpp> #include "logger.h" namespace logging = boost::log; namespace attrs = boost::log::attributes; namespace src = boost::log::sources; namespace sinks = boost::log::sinks; namespace expr = boost::log::expressions; namespace keywords = boost::log::keywords; using boost::shared_ptr; std::shared_ptr<Logger> Logger::m_instance = {nullptr}; template <typename CharT, typename TraitsT> inline std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& strm, LogLevel lvl) { static const char* const str[] = { "ALL", "TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "OFF"}; if (static_cast<std::size_t>(lvl) < (sizeof(str) / sizeof(*str))) { strm << str[static_cast<int>(lvl)]; } else { strm << static_cast<int>(lvl); } return strm; } Logger::Logger() { logging::add_console_log(std::clog, keywords::format = (expr::stream << expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") << "[" << expr::attr<attrs::current_thread_id::value_type>("ThreadID") << "] " << expr::attr<LogLevel>("Severity") << " - " << expr::smessage) ); logging::add_common_attributes(); } Logger* Logger::Instance() { if (m_instance == nullptr) { m_instance.reset(new Logger); } return m_instance.get(); } boost::log::sources::severity_logger_mt<LogLevel>& Logger::GetLogger() { return m_logger; }
测试项目(log-test)代码:
main.cpp
#include "logger.h" #include <iostream> #include <string> int main(int argc, char* argv[]) { Logger::Instance(); BOOST_LOG_SEV(Logger::Instance()->GetLogger(),LogLevel::LOG_INFO) << "test"; }
问题原因与解决方案
1. 日志级别输出运算符的可见性与实例化问题
你定义的operator<<模板函数仅存在于共享库的logger.cpp中,测试程序编译时无法看到这个模板的定义,会自行生成一个该模板的实例。这导致共享库和测试程序中存在两个完全独立的operator<<实例,Boost.Log在跨模块调用时无法正确传递日志级别值,最终表现为lvl始终为0。
修改方法:
- 将
operator<<的声明移到logger.h中,并添加导出标记:// 在logger.h的LogLevel枚举之后添加 template <typename CharT, typename TraitsT> LOGGER_EXPORT std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& strm, LogLevel lvl); - 在
logger.cpp中保留模板定义,并显式实例化常用的流版本(确保共享库中包含这些实例,避免测试程序重复生成):// 在logger.cpp的模板定义之后添加 template std::ostream& operator<<(std::ostream&, LogLevel); template std::wostream& operator<<(std::wostream&, LogLevel);
2. Boost.Log类型属性的一致性问题
Boost.Log依赖类型信息来识别和管理日志属性,若共享库与测试程序中LogLevel类型的RTTI信息不一致(比如编译选项差异导致类型被视为不同实体),会导致Severity属性无法正确匹配,进而获取到错误的级别值。
验证与修复:
- 确保共享库和测试程序的编译选项一致,尤其是RTTI相关设置(如VC++的
/GR选项)。 - 确认
LogLevel枚举的定义在头文件中被正确包含,且没有在不同模块中重复定义。
3. 日志器属性的全局注册问题
add_common_attributes()在共享库中调用后,测试程序中无需再次调用,但要确保共享库初始化时已完成所有属性注册。当前单例模式的初始化时机是首次调用Instance(),这部分逻辑是正确的,无需修改。
完成上述修改后,测试程序就能正确识别共享库中定义的日志级别输出运算符,与Boost.Log的属性系统正确交互,日志级别将正常显示。
内容的提问来源于stack exchange,提问作者behtgod
相关产品推荐
相关产品推荐

