log4cxx访问异常,使用<<运算符和宏

log4cxx access exception using << operator and macro

本文关键字:lt 运算符 访问 使用 log4cxx 异常      更新时间:2023-10-16

我试图通过使用#define指令和宏在代码中使用log4cxx进行封装。代码进行了编译,但在运行时遇到了访问冲突,因为我认为流对象没有正确初始化。

试图使log4cxx可插入的头文件如下所示:

#ifdef USE_LOG4CXX
#include "log4cxx/logger.h"
#define LOG_DEBUG(msg) LOG4CXX_DEBUG(logger, msg)
#define LOG_INFO(msg) LOG4CXX_INFO(logger, msg)
#define LOG_WARN(msg) LOG4CXX_WARN(logger, msg)
#define LOG_ERROR(msg) LOG4CXX_ERROR(logger, msg)
#define LOGGER_DECL static const log4cxx::LoggerPtr logger
#define LOGGER_INIT(source,name) const log4cxx::LoggerPtr source::logger = log4cxx::Logger::getLogger(#name); 
#else  // use some other log method, e.g. stdout which worked fine

.cpp文件的日志记录如下:

LOG_INFO("state(): " << old_state << " ==> " << new_state );

预处理器将.cpp文件扩展为:

{ if (logger->isInfoEnabled()) { ::log4cxx::helpers::MessageBuffer oss_; logger->forcedLog(::log4cxx::Level::getInfo(), oss_.str(oss_ << "state(): " << state_ << " ==> " << new_state), ::log4cxx::spi::LocationInfo("c:\dev\ezx-capi\iserver-api\iserver_client.cpp",  __FUNCSIG__  , 190)); }};

old_state和new_state的数据类型为int

运行时,应用程序在以下位置失败:

std::basic_ostream<char>& operator<<(CharMessageBuffer& os, const V& val) {
   return ((std::basic_ostream<char>&) os) << val;
}

在调试器中,问题看起来像是CharMessageBuffer对象有一个未初始化的std::basic_streambuf成员,因此当它附加值时,它就死了。(不过我不确定这个解释。)

一路往下钻,它在std::basic_ostream:中死亡

_Myt& __CLR_OR_THIS_CALL operator<<(int _Val)
    {   // insert an int
    ios_base::iostate _State = ios_base::goodbit;
    const sentry _Ok(*this);  // dies right here, in the constructor

无论如何,我意识到这与我如何使用宏来调用LOG4CXX有关。(当我没有定义USE_LOG4FXX时,所有的日志语句都转到std::cout,它运行得很好。)

更新

另一条信息——似乎只有当我从静态库中调用日志记录时,这才是失败的。如果我使用EXE项目中相同(或相似)的宏,它根本不会失败。因此,我似乎无法在某种单独的测试应用程序中复制这个问题。

问题是由枚举的解释方式引起的。编译器调用了模板<<运算符,而不是采用int类型的<<运算符版本。更奇怪的是(对我来说),我写了一个测试应用程序,看看枚举数据类型是否是问题所在,它工作起来没有问题,即:

ezx::net::client_state::state mystate = ezx::net::client_state::connecting;
LOG_INFO("this should show new state" << mystate);

这没有引发任何错误,并且采用了与上面相同代码不同的代码路径。

我得出的结论是,这个运算符的log4cxx实现是脆弱的,因为它可以很好地编译,但在运行时会意外失败,这取决于数据类型是否被调度到正确版本的运算符。