0

我尝试通过使用#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 项目的相同(或类似)宏,它根本不会失败。所以我似乎无法在某种单独的测试应用程序中复制这个问题。

4

1 回答 1

0

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

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

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

我得出的结论是,该运算符的 log4cxx 实现是脆弱的,因为它可以正常编译,但在运行时会意外失败,具体取决于数据类型是否分派到正确版本的运算符。

于 2013-07-18T23:09:30.360 回答