logging-log4cxx-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "George Mardale" <...@delsyne.ro>
Subject unicode support (wofstream and codecvt)
Date Mon, 12 Jul 2004 12:47:07 GMT
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?"));
 }
}

 
Mime
View raw message