You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by sw...@apache.org on 2026/10/15 01:05:08 UTC

(logging-log4cxx) branch master updated: Simplify the FileAppender class interfaces (#779)

This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/master by this push:
     new 6f63a09b Simplify the FileAppender class interfaces (#779)
6f63a09b is described below

commit 6f63a09b769e9566726aa1aea297d2dae325f0e6
Author: Stephen Webb <st...@ieee.org>
AuthorDate: Thu Oct 15 12:05:03 2026 +1100

    Simplify the FileAppender class interfaces (#779)
    
    * Log a warning when an encoding option is unrecognised
    
    * Fix missing context message when internal logging with an exception
---
 src/main/cpp/charsetencoder.cpp                    |   2 +-
 src/main/cpp/consoleappender.cpp                   |   4 +-
 src/main/cpp/file.cpp                              |   4 +
 src/main/cpp/fileappender.cpp                      | 141 +++++++--------------
 src/main/cpp/fileoutputstream.cpp                  |   6 -
 src/main/cpp/loglog.cpp                            |   1 +
 src/main/cpp/multiprocessrollingfileappender.cpp   |  40 +++---
 src/main/cpp/rollingfileappender.cpp               |  70 ++++------
 src/main/cpp/writerappender.cpp                    |  35 ++---
 src/main/include/log4cxx/file.h                    |   5 +
 src/main/include/log4cxx/fileappender.h            |  29 +----
 .../include/log4cxx/helpers/fileoutputstream.h     |   1 -
 .../include/log4cxx/private/fileappender_priv.h    |  16 ++-
 .../log4cxx/private/rollingfileappender_priv.h     |   4 +
 .../include/log4cxx/private/writerappender_priv.h  |  13 +-
 .../rolling/multiprocessrollingfileappender.h      |  12 --
 .../include/log4cxx/rolling/rollingfileappender.h  |  19 ---
 src/main/include/log4cxx/writerappender.h          |  10 --
 18 files changed, 140 insertions(+), 272 deletions(-)

diff --git a/src/main/cpp/charsetencoder.cpp b/src/main/cpp/charsetencoder.cpp
index e3159294..31f38cc8 100644
--- a/src/main/cpp/charsetencoder.cpp
+++ b/src/main/cpp/charsetencoder.cpp
@@ -713,7 +713,7 @@ CharsetEncoderPtr CharsetEncoder::getEncoder(const LogString& charset)
 #if APR_HAS_XLATE
 	return std::make_shared<APRCharsetEncoder>(charset);
 #else
-	throw IllegalArgumentException(charset);
+	throw IllegalArgumentException(charset + LOG4CXX_STR(": unsupported encoding"));
 #endif
 }
 
diff --git a/src/main/cpp/consoleappender.cpp b/src/main/cpp/consoleappender.cpp
index dbb4e537..8f16bc96 100644
--- a/src/main/cpp/consoleappender.cpp
+++ b/src/main/cpp/consoleappender.cpp
@@ -50,7 +50,7 @@ ConsoleAppender::ConsoleAppender(const LayoutPtr& layout)
 {
 	setLayout(layout);
 	_priv->setWriter(std::make_shared<SystemOutWriter>());
-	_priv->activateOptions();
+	_priv->checkConfiguration();
 }
 
 ConsoleAppender::ConsoleAppender(const LayoutPtr& layout, const LogString& target)
@@ -122,7 +122,7 @@ void ConsoleAppender::activateOptions(  )
 		_priv->setWriter(std::make_shared<SystemErrWriter>());
 	}
 
-	_priv->activateOptions();
+	_priv->checkConfiguration();
 }
 
 void ConsoleAppender::setOption(const LogString& option, const LogString& value)
diff --git a/src/main/cpp/file.cpp b/src/main/cpp/file.cpp
index cc4252bf..476898dd 100644
--- a/src/main/cpp/file.cpp
+++ b/src/main/cpp/file.cpp
@@ -139,6 +139,10 @@ File::~File()
 	}
 }
 
+bool File::hasPath() const
+{
+	return !m_priv->path.empty();
+}
 
 LogString File::getPath() const
 {
diff --git a/src/main/cpp/fileappender.cpp b/src/main/cpp/fileappender.cpp
index 65fd53d2..9d5b3954 100644
--- a/src/main/cpp/fileappender.cpp
+++ b/src/main/cpp/fileappender.cpp
@@ -53,7 +53,7 @@ FileAppender::FileAppender
 	)
 	: WriterAppender(std::make_unique<FileAppenderPriv>(layout1, fileName1, append1, bufferedIO1, bufferSize1))
 {
-	activateOptionsInternal();
+	_priv->activate();
 }
 
 FileAppender::FileAppender
@@ -63,13 +63,13 @@ FileAppender::FileAppender
 	)
 	: WriterAppender(std::make_unique<FileAppenderPriv>(layout1, fileName1, append1, false))
 {
-	activateOptionsInternal();
+	_priv->activate();
 }
 
 FileAppender::FileAppender(const LayoutPtr& layout1, const LogString& fileName1)
 	: WriterAppender(std::make_unique<FileAppenderPriv>(layout1, fileName1))
 {
-	activateOptionsInternal();
+	_priv->activate();
 }
 
 FileAppender::FileAppender(std::unique_ptr<FileAppenderPriv> priv)
@@ -94,23 +94,13 @@ void FileAppender::setAppend(bool fileAppend1)
 void FileAppender::setFile(const LogString& file)
 {
 	std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
-	setFileInternal(file);
-}
-
-void FileAppender::setFileInternal(const LogString& file)
-{
-	_priv->fileName = file;
+	_priv->targetFile.setPath(file);
 }
 
 void FileAppender::setBufferedIO(bool bufferedIO1)
 {
 	std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
 	_priv->bufferedIO = bufferedIO1;
-
-	if (bufferedIO1)
-	{
-		setImmediateFlush(false);
-	}
 }
 
 void FileAppender::setOption(const LogString& option,
@@ -120,7 +110,7 @@ void FileAppender::setOption(const LogString& option,
 		|| StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FILENAME"), LOG4CXX_STR("filename")))
 	{
 		std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
-		_priv->fileName = stripDuplicateBackslashes(value);
+		_priv->targetFile.setPath(stripDuplicateBackslashes(value));
 	}
 	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("APPEND"), LOG4CXX_STR("append")))
 	{
@@ -162,66 +152,67 @@ void FileAppender::setOption(const LogString& option,
 void FileAppender::activateOptions(  )
 {
 	std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
-	activateOptionsInternal();
+	// It does not make sense to have immediate flush and bufferedIO.
+	if (_priv->bufferedIO)
+	   setImmediateFlush(false);
+	_priv->activate();
 }
 
-void FileAppender::activateOptionsInternal()
+void FileAppender::FileAppenderPriv::activate()
 {
 	int errors = 0;
 
-	if (!_priv->fileName.empty())
+	if (this->targetFile.hasPath())
 	{
 		try
 		{
-			setFileInternal(_priv->fileName, _priv->fileAppend, _priv->bufferedIO, _priv->bufferSize);
+			this->open();
 		}
 		catch (IOException& e)
 		{
 			errors++;
 			LogString msg(LOG4CXX_STR("setFile("));
-			msg.append(_priv->fileName);
+			msg.append(this->targetFile.getPath());
 			msg.append(1, (logchar) 0x2C /* ',' */);
-			StringHelper::toString(_priv->fileAppend, msg);
+			StringHelper::toString(this->fileAppend, msg);
 			msg.append(LOG4CXX_STR(") call failed."));
-			_priv->errorHandler->error(msg, e, ErrorCode::FILE_OPEN_FAILURE);
+			this->errorHandler->error(msg, e, ErrorCode::FILE_OPEN_FAILURE);
 		}
 	}
 	else
 	{
 		errors++;
 		LogLog::error(LogString(LOG4CXX_STR("File option not set for appender ["))
-			+  _priv->name + LOG4CXX_STR("]."));
+			+  this->name + LOG4CXX_STR("]."));
 		LogLog::warn(LOG4CXX_STR("Are you using FileAppender instead of ConsoleAppender?"));
 	}
 
 	if (errors == 0)
 	{
-		_priv->activateOptions();
-		if (auto p = _priv->taskManager.lock())
-			p->value().removePeriodicTask(_priv->flushTaskName);
+		this->checkConfiguration();
+		if (auto p = this->taskManager.lock())
+			p->value().removePeriodicTask(this->flushTaskName);
 
-		if (!_priv->bufferedIO)
+		if (!this->bufferedIO)
 			;
-		else if (0 < _priv->bufferedSeconds)
+		else if (0 < this->bufferedSeconds)
 		{
 			auto taskManager = ThreadUtility::instancePtr();
-			if (!_priv->flushTask)
+			if (!this->flushTask)
 			{
-				_priv->flushTask = std::make_shared<FileAppenderPriv::FlushTask>();
-				_priv->flushTask->priv = _priv;
+				this->flushTask = std::make_shared<FileAppenderPriv::FlushTask>();
+				this->flushTask->priv = this;
 			}
-			// Key the task by a unique name: the appender may be renamed after
-			// activation or share its name with another appender, and the
-			// destructor must deregister this task, not another appender's.
-			_priv->flushTaskName = getName();
-			_priv->flushTaskName.append(1, (logchar) 0x23 /* '#' */);
-			StringHelper::toString(reinterpret_cast<size_t>(m_priv.get()), _priv->flushTaskName);
-			auto flushTask = _priv->flushTask;
-			taskManager->value().addPeriodicTask(_priv->flushTaskName
+			// Key the task by a unique name
+			this->flushTaskName = this->targetFile.getName();
+			this->flushTaskName.append(1, (logchar) 0x23 /* '#' */);
+			StringHelper::toString(reinterpret_cast<size_t>(this), this->flushTaskName);
+			auto flushTask = this->flushTask;
+			taskManager->value().addPeriodicTask(this->flushTaskName
 				, [flushTask]() { flushTask->run(); }
-				, std::chrono::seconds(_priv->bufferedSeconds)
+				, std::chrono::seconds(this->bufferedSeconds)
 				);
-			_priv->taskManager = taskManager;
+			this->taskManager = taskManager;
 		}
 	}
 }
@@ -281,53 +272,21 @@ LogString FileAppender::stripDuplicateBackslashes(const LogString& src)
 	return src;
 }
 
-/**
-  <p>Sets and <i>opens</i> the file where the log output will
-  go. The specified file must be writable.
-
-  <p>If there was already an opened file, then the previous file
-  is closed first.
-
-  <p><b>Do not use this method directly. To configure a FileAppender
-  or one of its subclasses, set its properties one by one and then
-  call activateOptions.</b>
-
-  @param filename The path to the log file.
-  @param append   If true will append to fileName. Otherwise will
-      truncate fileName.
-  @param bufferedIO
-  @param bufferSize
-
-  @throws IOException
-
- */
-void FileAppender::setFileInternal(
-	const LogString& filename,
-	bool append1,
-	bool bufferedIO1,
-	size_t bufferSize1)
+void FileAppender::FileAppenderPriv::open()
 {
-	// It does not make sense to have immediate flush and bufferedIO.
-	if (bufferedIO1)
-	{
-		setImmediateFlush(false);
-	}
-
-	_priv->close();
+	this->close();
 
 	bool writeBOM = false;
 
-	if (StringHelper::equalsIgnoreCase(getEncoding(),
+	if (StringHelper::equalsIgnoreCase(this->encoding,
 			LOG4CXX_STR("utf-16"), LOG4CXX_STR("UTF-16")))
 	{
 		//
 		//    don't want to write a byte order mark if the file exists
 		//
-		if (append1)
+		if (this->fileAppend)
 		{
-			File outFile;
-			outFile.setPath(filename);
-			writeBOM = !outFile.exists();
+			writeBOM = !this->targetFile.exists();
 		}
 		else
 		{
@@ -339,20 +298,19 @@ void FileAppender::setFileInternal(
 
 	try
 	{
-		outStream = std::make_shared<FileOutputStream>(filename, append1);
+		outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend);
 	}
 	catch (IOException&)
 	{
-		LogString parentName = File().setPath(filename).getParent();
+		LogString parentName = this->targetFile.getParent();
 
 		if (!parentName.empty())
 		{
-			File parentDir;
-			parentDir.setPath(parentName);
+			File parentDir(parentName);
 
 			if (!parentDir.exists() && parentDir.mkdirs())
 			{
-				outStream = std::make_shared<FileOutputStream>(filename, append1);
+				outStream = std::make_shared<FileOutputStream>(this->targetFile.getPath(), this->fileAppend);
 			}
 			else
 			{
@@ -377,27 +335,22 @@ void FileAppender::setFileInternal(
 		outStream->write(buf);
 	}
 
-	WriterPtr newWriter(createWriter(outStream));
+	WriterPtr newWriter(this->createWriter(outStream));
 
-	if (bufferedIO1)
+	if (this->bufferedIO)
 	{
-		newWriter = std::make_shared<BufferedWriter>(newWriter, bufferSize1);
+		newWriter = std::make_shared<BufferedWriter>(newWriter, this->bufferSize);
 	}
 
-	_priv->setWriter(newWriter);
+	this->setWriter(newWriter);
 
-	_priv->fileAppend = append1;
-	_priv->bufferedIO = bufferedIO1;
-	_priv->fileName = filename;
-	const size_t intMax = static_cast<size_t>((std::numeric_limits<int>::max)());
-	_priv->bufferSize = static_cast<int>(bufferSize1 > intMax ? intMax : bufferSize1);
-	_priv->writeHeader();
+	this->writeHeader();
 
 }
 
 LogString FileAppender::getFile() const
 {
-	return _priv->fileName;
+	return _priv->targetFile.getPath();
 }
 
 bool FileAppender::getBufferedIO() const
diff --git a/src/main/cpp/fileoutputstream.cpp b/src/main/cpp/fileoutputstream.cpp
index e72dd595..0264b61f 100644
--- a/src/main/cpp/fileoutputstream.cpp
+++ b/src/main/cpp/fileoutputstream.cpp
@@ -45,12 +45,6 @@ FileOutputStream::FileOutputStream(const LogString& filename, bool append)
 	m_priv->open(append);
 }
 
-FileOutputStream::FileOutputStream(const logchar* filename,	bool append)
-	: m_priv(std::make_unique<FileOutputStreamPrivate>(filename))
-{
-	m_priv->open(append);
-}
-
 void FileOutputStream::FileOutputStreamPrivate::open(bool append)
 {
 	apr_fileperms_t perm = APR_OS_DEFAULT;
diff --git a/src/main/cpp/loglog.cpp b/src/main/cpp/loglog.cpp
index f8da835f..1b790a28 100644
--- a/src/main/cpp/loglog.cpp
+++ b/src/main/cpp/loglog.cpp
@@ -294,6 +294,7 @@ void LogLog::LogLogPrivate::emit_log(const LogString& prefix, const LogString& m
 		exOut.append(out);
 		Transcoder::decode(raw, exOut);
 	}
+	out.append(msg);
 	out.append(suffix);
 	out.append(1, (logchar) 0x0A);
 
diff --git a/src/main/cpp/multiprocessrollingfileappender.cpp b/src/main/cpp/multiprocessrollingfileappender.cpp
index 8ef5478c..92add2f2 100644
--- a/src/main/cpp/multiprocessrollingfileappender.cpp
+++ b/src/main/cpp/multiprocessrollingfileappender.cpp
@@ -136,6 +136,8 @@ public: // Support classes
 	public: // Accessors
 		bool hasLock() const { return m_ok; }
 	};
+
+	helpers::WriterPtr createWriter(const helpers::OutputStreamPtr& os) override;
 };
 
 #define _priv static_cast<MultiprocessRollingFileAppenderPriv*>(m_priv.get())
@@ -157,13 +159,13 @@ void MultiprocessRollingFileAppender::activateOptions(  )
 {
 	if (_priv->activateOptions())
 	{
-		// FileAppender::activateOptionsInternal() closes any existing writer,
+		// FileAppenderPriv::activate() closes any existing writer,
 		// destroying the FileOutputStream (and the pool) that owns the cached
 		// apr_file_t, before opening the new file (which may fail).  Clear the
 		// cache first so a failed open cannot leave it dangling;
 		// createWriter() re-caches the handle on success.
 		_priv->log_file = NULL;
-		FileAppender::activateOptionsInternal();
+		_priv->activate();
 	}
 
 	if (auto pTimeBased = LOG4CXX_NS::cast<TimeBasedRollingPolicy>(_priv->rollingPolicy))
@@ -335,18 +337,15 @@ bool MultiprocessRollingFileAppender::synchronizedRollover(const TriggeringPolic
 					msg.append(LOG4CXX_STR("] failed"));
 					_priv->errorHandler->error(msg);
 				}
-				setFileInternal(rollover1->getActiveFileName(), appendToExisting, _priv->bufferedIO, _priv->bufferSize);
+				_priv->targetFile.setPath(rollover1->getActiveFileName());
+				_priv->fileAppend = appendToExisting;
+				_priv->activate();
 			}
 			else
 			{
-				setFileInternal(rollover1->getActiveFileName());
-				// Call activateOptions to create any intermediate directories(if required)
-				FileAppender::activateOptionsInternal();
-				OutputStreamPtr os = std::make_shared<FileOutputStream>
-					( rollover1->getActiveFileName()
-					, rollover1->getAppend()
-					);
-				_priv->setWriter(createWriter(os));
+				_priv->targetFile.setPath(rollover1->getActiveFileName());
+				_priv->fileAppend = rollover1->getAppend();
+				_priv->activate();
 				bool success = true; // A synchronous action is not required
 				if (auto pAction = rollover1->getSynchronous())
 					success = pAction->execute();
@@ -402,7 +401,7 @@ void MultiprocessRollingFileAppender::reopenFile(const LogString& fileName)
 		pTimeBased->loadLastFileName();
 #endif
 	OutputStreamPtr os = std::make_shared<FileOutputStream>(fileName, true);
-	WriterPtr newWriter(createWriter(os));
+	WriterPtr newWriter(_priv->createWriter(os));
 	setFile(fileName);
 	_priv->setWriter(newWriter);
 }
@@ -443,27 +442,18 @@ void MultiprocessRollingFileAppender::subAppend( const spi::LoggingEventPtr& eve
 	FileAppender::subAppend( event );
 }
 
-/**
-   Returns an OutputStreamWriter when passed an OutputStream.  The
-   encoding used will depend on the value of the
-   <code>encoding</code> property.  If the encoding value is
-   specified incorrectly the writer will be opened using the default
-   system encoding (an error message will be printed to the loglog.
- @param os output stream, may not be null.
- @return new writer.
- */
-WriterPtr MultiprocessRollingFileAppender::createWriter(const OutputStreamPtr& os)
+WriterPtr MultiprocessRollingFileAppender::MultiprocessRollingFileAppenderPriv::createWriter(const OutputStreamPtr& os)
 {
 	auto fos = LOG4CXX_NS::cast<FileOutputStream>(os);
 	if( fos )
-		_priv->log_file = fos->getFilePtr();
+		this->log_file = fos->getFilePtr();
 	else
 	{
 		LogString msg(LOG4CXX_STR("Can't cast stream to FileOutputStream"));
 		msg += LOG4CXX_STR(" - Rollover synchronization will be degraded.");
-		_priv->errorHandler->error(msg);
+		this->errorHandler->error(msg);
 	}
-	return RollingFileAppender::createWriter(os);
+	return RollingFileAppenderPriv::createWriter(os);
 }
 
 
diff --git a/src/main/cpp/rollingfileappender.cpp b/src/main/cpp/rollingfileappender.cpp
index 3cc1f486..4d381538 100644
--- a/src/main/cpp/rollingfileappender.cpp
+++ b/src/main/cpp/rollingfileappender.cpp
@@ -184,7 +184,7 @@ void RollingFileAppender::activateOptions(  )
 {
 	if (_priv->activateOptions())
 	{
-		FileAppender::activateOptionsInternal();
+		_priv->activate();
 	}
 }
 
@@ -196,14 +196,14 @@ bool RollingFileAppender::RollingFileAppenderPriv::activateOptions()
 		LogLog::warn(LOG4CXX_STR("No rolling policy configured for the appender named [")
 			+ this->name + LOG4CXX_STR("]."));
 		auto fwrp = std::make_shared<FixedWindowRollingPolicy>();
-		fwrp->setFileNamePattern(this->fileName + LOG4CXX_STR(".%i"));
+		fwrp->setFileNamePattern(this->targetFile.getPath() + LOG4CXX_STR(".%i"));
 		this->rollingPolicy = fwrp;
 	}
 	else if (auto fwrp = LOG4CXX_NS::cast<FixedWindowRollingPolicy>(this->rollingPolicy))
 	{
 		// Was fwrp created to store the maximum index before the file name was set?
 		if (fwrp->getFileNamePattern() == LOG4CXX_STR(".%i"))
-			fwrp->setFileNamePattern(this->fileName + LOG4CXX_STR(".%i"));
+			fwrp->setFileNamePattern(this->targetFile.getPath() + LOG4CXX_STR(".%i"));
 	}
 
 	//
@@ -234,7 +234,7 @@ bool RollingFileAppender::RollingFileAppenderPriv::activateOptions()
 		try
 		{
 			RolloverDescriptionPtr rollover1 =
-				this->rollingPolicy->initialize(this->fileName, this->fileAppend);
+				this->rollingPolicy->initialize(this->targetFile.getPath(), this->fileAppend);
 
 			if (rollover1 != NULL)
 			{
@@ -245,7 +245,7 @@ bool RollingFileAppender::RollingFileAppenderPriv::activateOptions()
 					syncAction->execute();
 				}
 
-				this->fileName = rollover1->getActiveFileName();
+				this->targetFile.setPath(rollover1->getActiveFileName());
 				this->fileAppend = rollover1->getAppend();
 
 				//
@@ -259,12 +259,9 @@ bool RollingFileAppender::RollingFileAppenderPriv::activateOptions()
 				}
 			}
 
-			File activeFile;
-			activeFile.setPath(this->fileName);
-
 			if (this->fileAppend)
 			{
-				this->fileLength = activeFile.length();
+				this->fileLength = this->targetFile.length();
 			}
 			else
 			{
@@ -275,7 +272,7 @@ bool RollingFileAppender::RollingFileAppenderPriv::activateOptions()
 		}
 		catch (std::exception& ex)
 		{
-			LogLog::warn(LOG4CXX_STR("Exception activating RollingFileAppender ") + this->fileName, ex);
+			LogLog::warn(LOG4CXX_STR("Exception activating RollingFileAppender ") + this->targetFile.getPath(), ex);
 		}
 	}
 	return result;
@@ -369,19 +366,15 @@ bool RollingFileAppender::rolloverInternal()
 									}
 								}
 							}
-							setFileInternal(rollover1->getActiveFileName(), appendToExisting, _priv->bufferedIO, _priv->bufferSize);
+							_priv->targetFile.setPath(rollover1->getActiveFileName());
+							_priv->fileAppend = appendToExisting;
+							_priv->activate();
 						}
 						else
 						{
-							_priv->close();
-							setFileInternal(rollover1->getActiveFileName());
-							// Call activateOptions to create any intermediate directories(if required)
-							FileAppender::activateOptionsInternal();
-							OutputStreamPtr os = std::make_shared<FileOutputStream>
-									( rollover1->getActiveFileName()
-									, rollover1->getAppend()
-									);
-							_priv->setWriter(createWriter(os));
+							_priv->targetFile.setPath(rollover1->getActiveFileName());
+							_priv->fileAppend = rollover1->getAppend();
+							_priv->activate();
 
 							bool success = true;
 
@@ -524,7 +517,7 @@ namespace rolling
  * Wrapper for OutputStream that will report all write
  * operations back to this class for file length calculations.
  */
-class CountingOutputStream : public OutputStream
+class RollingFileAppender::RollingFileAppenderPriv::CountingOutputStream : public OutputStream
 {
 		/**
 		 * Wrapped output stream.
@@ -535,20 +528,20 @@ class CountingOutputStream : public OutputStream
 		/**
 		 * Rolling file appender to inform of stream writes.
 		 */
-		RollingFileAppender* rfa;
+		RollingFileAppenderPriv* parent;
 
 	public:
 		/**
 		 * Constructor.
 		 * @param os output stream to wrap.
-		 * @param rfa rolling file appender to inform.
+		 * @param parent rolling file appender to inform.
 		 */
 		CountingOutputStream
 			( const OutputStreamPtr& os1
-			, RollingFileAppender* rfa1
+			, RollingFileAppender::RollingFileAppenderPriv* parent1
 			)
 			: os(os1)
-			, rfa(rfa1)
+			, parent(parent1)
 		{
 		}
 
@@ -558,7 +551,7 @@ class CountingOutputStream : public OutputStream
 		void close(  ) override
 		{
 			os->close();
-			rfa = 0;
+			parent = 0;
 		}
 
 		/**
@@ -576,28 +569,19 @@ class CountingOutputStream : public OutputStream
 		{
 			os->write(buf);
 
-			if (rfa != 0)
+			if (parent != 0)
 			{
-				rfa->incrementFileLength(buf.limit());
+				parent->fileLength += buf.limit();
 			}
 		}
 };
 }
 }
 
-/**
-   Returns an OutputStreamWriter when passed an OutputStream.  The
-   encoding used will depend on the value of the
-   <code>encoding</code> property.  If the encoding value is
-   specified incorrectly the writer will be opened using the default
-   system encoding (an error message will be printed to the loglog.
- @param os output stream, may not be null.
- @return new writer.
- */
-WriterPtr RollingFileAppender::createWriter(const OutputStreamPtr& os)
+WriterPtr RollingFileAppender::RollingFileAppenderPriv::createWriter(const OutputStreamPtr& os)
 {
 	OutputStreamPtr cos = std::make_shared<CountingOutputStream>(os, this);
-	return FileAppender::createWriter(cos);
+	return FileAppenderPriv::createWriter(cos);
 }
 
 /**
@@ -609,11 +593,3 @@ size_t RollingFileAppender::getFileLength() const
 	return _priv->fileLength;
 }
 
-/**
- * Increments estimated byte length of current active log file.
- * @param increment additional bytes written to log file.
- */
-void RollingFileAppender::incrementFileLength(size_t increment)
-{
-	_priv->fileLength += increment;
-}
diff --git a/src/main/cpp/writerappender.cpp b/src/main/cpp/writerappender.cpp
index c5fa1b83..3d6e800c 100644
--- a/src/main/cpp/writerappender.cpp
+++ b/src/main/cpp/writerappender.cpp
@@ -56,10 +56,10 @@ WriterAppender::~WriterAppender()
 
 void WriterAppender::activateOptions(  )
 {
-	_priv->activateOptions();
+	_priv->checkConfiguration();
 }
 
-void WriterAppender::WriterAppenderPriv::activateOptions()
+void WriterAppender::WriterAppenderPriv::checkConfiguration()
 {
 	if (this->layout == 0)
 	{
@@ -142,42 +142,31 @@ void WriterAppender::WriterAppenderPriv::close()
 
 }
 
-/**
-   Returns an OutputStreamWriter when passed an OutputStream.  The
-   encoding used will depend on the value of the
-   <code>encoding</code> property.  If the encoding value is
-   specified incorrectly the writer will be opened using the default
-   system encoding (an error message will be printed to the loglog.  */
-WriterPtr WriterAppender::createWriter(const OutputStreamPtr& os)
+WriterPtr WriterAppender::WriterAppenderPriv::createWriter(const OutputStreamPtr& os)
 {
-
-	LogString enc(getEncoding());
-
 	CharsetEncoderPtr encoder;
 
-	if (enc.empty())
+	if (this->encoding.empty())
 	{
 		encoder = CharsetEncoder::getDefaultEncoder();
 	}
-	else
+	else try
 	{
-		if (StringHelper::equalsIgnoreCase(enc,
+		if (StringHelper::equalsIgnoreCase(this->encoding,
 				LOG4CXX_STR("utf-16"), LOG4CXX_STR("UTF-16")))
 		{
 			encoder = CharsetEncoder::getEncoder(LOG4CXX_STR("UTF-16BE"));
 		}
 		else
 		{
-			encoder = CharsetEncoder::getEncoder(enc);
-		}
-
-		if (encoder == NULL)
-		{
-			encoder = CharsetEncoder::getDefaultEncoder();
-			LogLog::warn(LOG4CXX_STR("Error initializing output writer."));
-			LogLog::warn(LOG4CXX_STR("Unsupported encoding?"));
+			encoder = CharsetEncoder::getEncoder(this->encoding);
 		}
 	}
+	catch (const std::exception& ex)
+	{
+		LogLog::warn(LOG4CXX_STR("Reverting to default encoding"), ex);
+		encoder = CharsetEncoder::getDefaultEncoder();
+	}
 
 	return std::make_shared<OutputStreamWriter>(os, encoder);
 }
diff --git a/src/main/include/log4cxx/file.h b/src/main/include/log4cxx/file.h
index 65fac8d8..c8110d85 100644
--- a/src/main/include/log4cxx/file.h
+++ b/src/main/include/log4cxx/file.h
@@ -86,6 +86,11 @@ class LOG4CXX_EXPORT File
 		 */
 		~File();
 
+		/**
+		 *  Has a path been set?
+		 *  @return true if the file name is not empty.
+		 */
+		bool hasPath() const;
 		/**
 		 *  Determines if file exists.
 		 *  @return true if file exists.
diff --git a/src/main/include/log4cxx/fileappender.h b/src/main/include/log4cxx/fileappender.h
index 186230a0..06e76b9b 100644
--- a/src/main/include/log4cxx/fileappender.h
+++ b/src/main/include/log4cxx/fileappender.h
@@ -104,7 +104,7 @@ class LOG4CXX_EXPORT FileAppender : public WriterAppender
 
 		<p>Note: Actual opening of the file is made when
 		#activateOptions is called, not when the options are set.  */
-		virtual void setFile(const LogString& file);
+		void setFile(const LogString& file);
 
 		/**
 		Returns the value of the <b>Append</b> option.
@@ -218,33 +218,6 @@ class LOG4CXX_EXPORT FileAppender : public WriterAppender
 		 */
 		static LogString stripDuplicateBackslashes(const LogString& name);
 
-	protected:
-		void activateOptionsInternal();
-
-		/**
-		Sets and <i>opens</i> the file where the log output will
-		go. The specified file must be writable.
-
-		<p>If there was already an opened file, then the previous file
-		is closed first.
-
-		<p><b>Do not use this method directly. To configure a FileAppender
-		or one of its subclasses, set its properties one by one and then
-		call activateOptions.</b>
-
-		The mutex must be locked before calling this function.
-
-		@param file The path to the log file.
-		@param append If true will append to fileName. Otherwise will
-		truncate fileName.
-		@param bufferedIO Do we do bufferedIO?
-		@param bufferSize How big should the IO buffer be?
-		*/
-		void setFileInternal(const LogString& file, bool append,
-			bool bufferedIO, size_t bufferSize);
-
-		void setFileInternal(const LogString& file);
-
 	private:
 		FileAppender(const FileAppender&);
 		FileAppender& operator=(const FileAppender&);
diff --git a/src/main/include/log4cxx/helpers/fileoutputstream.h b/src/main/include/log4cxx/helpers/fileoutputstream.h
index 67308cef..f9b70247 100644
--- a/src/main/include/log4cxx/helpers/fileoutputstream.h
+++ b/src/main/include/log4cxx/helpers/fileoutputstream.h
@@ -44,7 +44,6 @@ class LOG4CXX_EXPORT FileOutputStream : public OutputStream
 		END_LOG4CXX_CAST_MAP()
 
 		FileOutputStream(const LogString& filename, bool append = false);
-		FileOutputStream(const logchar* filename, bool append = false);
 		virtual ~FileOutputStream();
 
 		using OutputStream::close;
diff --git a/src/main/include/log4cxx/private/fileappender_priv.h b/src/main/include/log4cxx/private/fileappender_priv.h
index 21b68dd4..986c5bbe 100644
--- a/src/main/include/log4cxx/private/fileappender_priv.h
+++ b/src/main/include/log4cxx/private/fileappender_priv.h
@@ -38,7 +38,7 @@ struct FileAppender::FileAppenderPriv : public WriterAppender::WriterAppenderPri
 		)
 		: WriterAppenderPriv(_layout)
 		, fileAppend(_fileAppend)
-		, fileName(_fileName)
+		, targetFile(_fileName)
 		, bufferedIO(_bufferedIO)
 		, bufferSize(_bufferSize)
 		{}
@@ -53,8 +53,8 @@ struct FileAppender::FileAppenderPriv : public WriterAppender::WriterAppenderPri
 	bool fileAppend;
 
 	/**
-	The name of the log file. */
-	LogString fileName;
+	The targeted file name. */
+	File targetFile;
 
 	/**
 	Do we do bufferedIO? */
@@ -113,6 +113,16 @@ struct FileAppender::FileAppenderPriv : public WriterAppender::WriterAppenderPri
 	Only used when <code>bufferedIO == true</code>.
 	*/
 	helpers::ThreadUtility::ManagerWeakPtr taskManager;
+
+	/**
+	Call open() and, if successful, set up any required monitoring
+	*/
+	void activate();
+
+	/**
+	Close any existing writer and establish a new writer
+	*/
+	void open();
 };
 
 }
diff --git a/src/main/include/log4cxx/private/rollingfileappender_priv.h b/src/main/include/log4cxx/private/rollingfileappender_priv.h
index f2c6e9a8..1dda474e 100644
--- a/src/main/include/log4cxx/private/rollingfileappender_priv.h
+++ b/src/main/include/log4cxx/private/rollingfileappender_priv.h
@@ -53,6 +53,10 @@ struct RollingFileAppender::RollingFileAppenderPriv : public FileAppenderPriv
 	spi::LoggingEventPtr _event;
 
 	bool activateOptions();
+
+	helpers::WriterPtr createWriter(const helpers::OutputStreamPtr& os) override;
+
+	class CountingOutputStream;
 };
 
 } // namespace rolling
diff --git a/src/main/include/log4cxx/private/writerappender_priv.h b/src/main/include/log4cxx/private/writerappender_priv.h
index 6263e570..8fc87112 100644
--- a/src/main/include/log4cxx/private/writerappender_priv.h
+++ b/src/main/include/log4cxx/private/writerappender_priv.h
@@ -106,7 +106,18 @@ struct WriterAppender::WriterAppenderPriv : public AppenderSkeleton::AppenderSke
 		this->writer = newWriter;
 	}
 
-	void activateOptions();
+	/**
+		Invoke the ErrorHandler if the layout or writer is null.
+	*/
+	void checkConfiguration();
+
+	/**
+		Returns a WriterPtr when passed an OutputStream
+		using \c encoding to create the character encoder.
+		A warning is written to the internal logging output
+		if \c encoding string is not recognised.
+	*/
+	virtual helpers::WriterPtr createWriter(const helpers::OutputStreamPtr& os);
 };
 
 }
diff --git a/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h b/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h
index 97f15199..2eed7d8a 100644
--- a/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h
+++ b/src/main/include/log4cxx/rolling/multiprocessrollingfileappender.h
@@ -82,18 +82,6 @@ class LOG4CXX_EXPORT MultiprocessRollingFileAppender : public RollingFileAppende
 		*/
 		void subAppend( const spi::LoggingEventPtr& event ) override;
 
-	protected:
-		/**
-		   Returns an OutputStreamWriter when passed an OutputStream.  The
-		   encoding used will depend on the value of the
-		   <code>encoding</code> property.  If the encoding value is
-		   specified incorrectly the writer will be opened using the default
-		   system encoding (an error message will be printed to the loglog.
-		 @param os output stream, may not be null.
-		 @return new writer.
-		 */
-		helpers::WriterPtr createWriter(const helpers::OutputStreamPtr& os) override;
-
 	private:
 		/**
 		 * Coordinate a rollover with other processes
diff --git a/src/main/include/log4cxx/rolling/rollingfileappender.h b/src/main/include/log4cxx/rolling/rollingfileappender.h
index 8a144ce9..ec3b050c 100644
--- a/src/main/include/log4cxx/rolling/rollingfileappender.h
+++ b/src/main/include/log4cxx/rolling/rollingfileappender.h
@@ -211,31 +211,12 @@ class LOG4CXX_EXPORT RollingFileAppender : public FileAppender
 		*/
 		void close() override;
 
-	protected:
-		/**
-		   Returns an OutputStreamWriter when passed an OutputStream.  The
-		   encoding used will depend on the value of the
-		   <code>encoding</code> property.  If the encoding value is
-		   specified incorrectly the writer will be opened using the default
-		   system encoding (an error message will be printed to the loglog.
-		 @param os output stream, may not be null.
-		 @return new writer.
-		 */
-		helpers::WriterPtr createWriter(const helpers::OutputStreamPtr& os) override;
-
 	public:
 		/**
 		 * Get byte length of current active log file.
 		 * @return byte length of current active log file.
 		 */
 		size_t getFileLength() const;
-
-		/**
-		 * Increments estimated byte length of current active log file.
-		 * @param increment additional bytes written to log file.
-		 */
-		void incrementFileLength(size_t increment);
-
 };
 
 LOG4CXX_PTR_DEF(RollingFileAppender);
diff --git a/src/main/include/log4cxx/writerappender.h b/src/main/include/log4cxx/writerappender.h
index a526dd28..b9a93eab 100644
--- a/src/main/include/log4cxx/writerappender.h
+++ b/src/main/include/log4cxx/writerappender.h
@@ -110,16 +110,6 @@ class LOG4CXX_EXPORT WriterAppender : public AppenderSkeleton
 		*/
 		void close() override;
 
-	protected:
-
-		/**
-		    Returns an OutputStreamWriter when passed an OutputStream.  The
-		    encoding used will depend on the value of the
-		    <code>encoding</code> property.  If the encoding value is
-		    specified incorrectly the writer will be opened using the default
-		    system encoding (an error message will be printed to the loglog.  */
-		virtual helpers::WriterPtr createWriter(const helpers::OutputStreamPtr& os);
-
 	public:
 		/**
 		The current encoding value.