I tried to encapsulate using log4cxx in our code by using #define
directives and macros. The code compiles but when running I get Access violation, because I believe the stream object is not correctly initialized.
The header file which tries to make log4cxx pluggable looks like this:
#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
The .cpp file does the logging like this:
LOG_INFO("state(): " << old_state << " ==> " << new_state );
The preprocessor expanded the .cpp file to:
{ 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)); }};
The data type of old_state and new_state are int
.
When running, the application fails on:
std::basic_ostream<char>& operator<<(CharMessageBuffer& os, const V& val) {
return ((std::basic_ostream<char>&) os) << val;
}
In the debugger, the problem looks like the CharMessageBuffer
object has a std::basic_streambuf
member which is not initialized, and so when it goes to append the value, it dies. (I'm not sure about that explanation though.)
Drilling all the way down, it dies in 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
Anyway, I realize this has something to do with how I am using a macro to invoke LOG4CXX. (When I had the USE_LOG4FXX not defined, all the log statements were going to std::cout
and it worked fine.)
UPDATE
One other piece of information - it seems that this is only failing when I invoke the logging from within a static library. If I use the same (or similar) macro from an EXE project, it doesn't fail at all. So I can't seem to replicate this problem in some kind of separate test application.