Hi everybody,
 
recently I had a problem when using log4cxx with unicode characters... I used version 0.9.7, RollingFileAppender as an appender and PatternLayout as a layout...
 
The problem appeared when I was logging 'special' characters (like Greek letters, for example). In this situation, log4cxx didn't log anything and looked like it was hanging/crashed, but it wasn't actually (the rest of the code executed correctly)... Moreover, the logfile itself didn't look like an UNICODE file... When I opened it with Notepad, the encoding type was ANSI (instead of UNICODE as I expected).
 
I searched the web and I found a similar problem when using wofstream and unicode characters: http://www.codeproject.com/vcpp/stl/upgradingstlappstounicode.asp.
 
It seems that the solution is to use a new codecvt-class that converts wchar_t to wchar_t (i.e. do nothing) and attach it to the wofstream object. I used this idea in RollingFileAppender.cpp and the problem was solved (i.e., I was able to log 'special' chars, like Greek chars + the encoding-type of the log file was UNICODE).
 
So, at the end of the email, I attached the modified version of RollingFileAdapter.cpp. If the log4cxx developers consider this change useful, they can use it in the next log4cxx release.
 
Also, I think there is still a problem when logging 'long' messages. When the message to be logged exceeds a certain size (I don't know for sure which size, but it looks like it's 1000+ chars), the log4cxx behavior is the same as described earlier (no subsequent log messages are logged into the file anymore)...
 
Did any of you encounter this behavior?
 
Best regards,
George.
 
 
PS1 - Here is the modified RollingFileAppender.cpp:
 
/*
 * Copyright 2003,2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include <log4cxx/fileappender.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/optionconverter.h>
 
#if defined(UNICODE) && defined(WIN32)
#include <fstream> //--ADDED BY GM 2004.06.23
#endif
 
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::spi;
 
//--SECTION ADDED BY GM 2004.06.23
#if defined(UNICODE) && defined(WIN32)
using namespace std;
typedef codecvt<wchar_t, char, mbstate_t> Mybase;
 
    // CLASS Simple_codecvt
 
class Simple_codecvt : public Mybase
{
public:
    typedef wchar_t  _E;
    typedef char  _To;
    typedef mbstate_t _St;
 
    explicit Simple_codecvt(size_t _R = 0)
        : Mybase(_R) {}
 
protected:
    virtual result do_in(_St& _State,
  const _To *_F1, const _To *_L1, const _To *&_Mid1,
  _E *F2, _E *_L2, _E *&_Mid2) const
 {return (noconv);}
 
    virtual result do_out(_St& _State,
  const _E *_F1, const _E *_L1, const _E *&_Mid1,
  _To *F2, _E *_L2, _To *&_Mid2) const
 {return (noconv);}
 
    virtual result do_unshift(_St& _State,
  _To *_F2, _To *_L2, _To *&_Mid2) const
 {return (noconv);}
 
    virtual int do_length(_St& _State, const _To *_F1,
  const _To *_L1, size_t _N2) const _THROW0()
 {return (_N2 < (size_t)(_L1 - _F1)
 ? _N2 : _L1 - _F1); }
 
    virtual bool do_always_noconv() const _THROW0()
 {return (true);}
 
    virtual int do_max_length() const _THROW0()
 {return (2);}
 
    virtual int do_encoding() const _THROW0()
 {return (2);}
 
};
#endif
//---END MODIFIED BY GM
 
IMPLEMENT_LOG4CXX_OBJECT(FileAppender)
 
FileAppender::FileAppender()
: fileAppend(true), bufferedIO(false), bufferSize(8*1024)
{
}
 
FileAppender::FileAppender(const LayoutPtr& layout, const String& fileName,
 bool append, bool bufferedIO, int bufferSize)
: fileAppend(true), bufferedIO(false), bufferSize(8*1024)
{
 this->layout = layout;
 this->setFile(fileName, append, bufferedIO, bufferSize);
}
 
FileAppender::FileAppender(const LayoutPtr& layout, const String& fileName,
 bool append)
: fileAppend(true), bufferedIO(false), bufferSize(8*1024)
{
 this->layout = layout;
 this->setFile(fileName, append, false, bufferSize);
}
 
FileAppender::FileAppender(const LayoutPtr& layout, const String& fileName)
: fileAppend(true), bufferedIO(false), bufferSize(8*1024)
{
 this->layout = layout;
 this->setFile(fileName, true, false, bufferSize);
}
 
FileAppender::~FileAppender()
{
 finalize();
}
 
void FileAppender::setFile(const String& file)
{
 // Trim spaces from both ends. The users probably does not want
 // trailing spaces in file names.
 fileName = StringHelper::trim(file);
}
 
void FileAppender::setFile(const String& fileName, bool append,
 bool bufferedIO, int bufferSize)
{
 synchronized sync(this);
 
 LOGLOG_DEBUG(_T("FileAppender::activateOptions called : ")
  << fileName << _T(", ") << append);
 // It does not make sense to have immediate flush and bufferedIO.
 if(bufferedIO)
 {
  setImmediateFlush(false);
 }
 
 if(ofs.is_open())
 {
  reset();
 }
 
/* if (bufferedIO && bufferSize > 0)
 {
  buffer = new char[bufferSize];
  out.rdbuf()->setbuf(buffer, 0);
 }*/
 
#if defined(UNICODE) && defined(WIN32) //--ADDED BY GM
 locale loc = _ADDFAC(locale::classic(), new Simple_codecvt);
 ofs.imbue(loc);
#endif
 
 USES_CONVERSION
 ofs.open(T2A(fileName.c_str()), (append ? std::ios::app :
  std::ios::trunc)|std::ios::out | std::ios_base::binary); //--ADDED BY GM (binary flag)
 
 if(!ofs.is_open())
 {
  throw RuntimeException();
 }
 
 this->os = &ofs;
    this->fileName = fileName;
    this->fileAppend = append;
    this->bufferedIO = bufferedIO;
    this->bufferSize = bufferSize;
 writeHeader();
 LogLog::debug(_T("FileAppender::setFile ended"));
}
 
void FileAppender::closeWriter()
{
 ofs.close();
 os = 0;
}
 
void FileAppender::closeFile()
{
 if (os != 0)
 {
  try
  {
   closeWriter();
  }
  catch(Exception& e)
  {
   LogLog::error(_T("Could not close file ") + fileName, e);
  }
 }
}
 
void FileAppender::setBufferedIO(bool bufferedIO)
{
 this->bufferedIO = bufferedIO;
 if(bufferedIO)
 {
  immediateFlush = false;
 }
}
 
void FileAppender::setOption(const String& option,
 const String& value)
{
 if (StringHelper::equalsIgnoreCase(option, _T("file"))
  || StringHelper::equalsIgnoreCase(option, _T("filename")))
 {
  fileName = value;
 }
 else if (StringHelper::equalsIgnoreCase(option, _T("append")))
 {
  fileAppend = OptionConverter::toBoolean(value, true);
 }
 else if (StringHelper::equalsIgnoreCase(option, _T("bufferedio")))
 {
  bufferedIO = OptionConverter::toBoolean(value, true);
 }
 else if (StringHelper::equalsIgnoreCase(option, _T("immediateflush")))
 {
  bufferedIO = !OptionConverter::toBoolean(value, false);
 }
 else if (StringHelper::equalsIgnoreCase(option, _T("buffersize")))
 {
  bufferSize = OptionConverter::toFileSize(value, 8*1024);
 }
 else
 {
  WriterAppender::setOption(name, value);
 }
}
 
void FileAppender::activateOptions()
{
 if (!fileName.empty())
 {
  try
  {
   setFile(fileName, fileAppend, bufferedIO, bufferSize);
  }
  catch(Exception& e)
  {
   errorHandler->error(_T("Unable to open file: ") + fileName,
   e, ErrorCode::FILE_OPEN_FAILURE);
  }
 }
 else
 {
  LogLog::warn(_T("File option not set for appender [")+name+_T("]."));
  LogLog::warn(_T("Are you using FileAppender instead of ConsoleAppender?"));
 }
}