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 9a59a4cb Ensure the AsyncAppender can fallback the appender on an
exception (#607)
9a59a4cb is described below
commit 9a59a4cbef39090a0743f129d1f79c9b71745536
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Mar 14 12:31:35 2026 +1100
Ensure the AsyncAppender can fallback the appender on an exception (#607)
* The logging event that caused the exception is sent to the backup
appender (as indicated in the documentation)
---
.github/workflows/log4cxx-macos.yml | 1 +
.github/workflows/log4cxx-msys2.yml | 2 +-
.github/workflows/log4cxx-ubuntu.yml | 1 +
.github/workflows/log4cxx-windows-static.yml | 10 +-
.github/workflows/log4cxx-windows.yml | 1 +
src/main/cpp/CMakeLists.txt | 14 ++-
src/main/cpp/appenderskeleton.cpp | 7 ++
src/main/cpp/asyncappender.cpp | 126 ++++++++++++++-------
src/main/cpp/fallbackerrorhandler.cpp | 35 ++++--
src/main/cpp/writerappender.cpp | 4 +
src/main/include/log4cxx/asyncappender.h | 5 -
.../log4cxx/private/appenderskeleton_priv.h | 4 +
.../include/log4cxx/varia/fallbackerrorhandler.h | 2 +-
src/test/cpp/CMakeLists.txt | 3 +
src/test/cpp/asyncappendertestcase.cpp | 86 +++++++++++++-
src/test/resources/input/xml/asyncWithFallback.xml | 25 ++++
16 files changed, 262 insertions(+), 64 deletions(-)
diff --git a/.github/workflows/log4cxx-macos.yml
b/.github/workflows/log4cxx-macos.yml
index e418a6fc..4a413637 100644
--- a/.github/workflows/log4cxx-macos.yml
+++ b/.github/workflows/log4cxx-macos.yml
@@ -69,6 +69,7 @@ jobs:
cd build
cmake \
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
+ -DLOG4CXX_TEST_ONLY_BUILD=1 \
-DLOG4CXX_ENABLE_ODBC=${{ matrix.odbc }} \
-DLOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER=${{ matrix.multiprocess
}} \
-DLOG4CXX_CFSTRING=ON \
diff --git a/.github/workflows/log4cxx-msys2.yml
b/.github/workflows/log4cxx-msys2.yml
index 26deced4..c9e0112b 100644
--- a/.github/workflows/log4cxx-msys2.yml
+++ b/.github/workflows/log4cxx-msys2.yml
@@ -69,7 +69,7 @@ jobs:
- name: 'configure and build'
shell: msys2 {0}
run: |
- cmake -G Ninja -S log4cxx -B log4cxx/build -DCMAKE_CXX_COMPILER=${{
matrix.cxx }} -DCMAKE_BUILD_TYPE=Debug
+ cmake -G Ninja -S log4cxx -B log4cxx/build -DLOG4CXX_TEST_ONLY_BUILD=1
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_BUILD_TYPE=Debug
cmake --build log4cxx/build
- name: 'run unit tests'
diff --git a/.github/workflows/log4cxx-ubuntu.yml
b/.github/workflows/log4cxx-ubuntu.yml
index e614ff8f..71899d10 100644
--- a/.github/workflows/log4cxx-ubuntu.yml
+++ b/.github/workflows/log4cxx-ubuntu.yml
@@ -109,6 +109,7 @@ jobs:
set -x
cmake \
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
+ -DLOG4CXX_TEST_ONLY_BUILD=1 \
-DLOG4CXX_ENABLE_ODBC=${{ matrix.odbc }} \
-DLOG4CXX_QT_SUPPORT=${{ matrix.qt }} \
-DENABLE_MULTITHREAD_TEST=${{ matrix.multithread }} \
diff --git a/.github/workflows/log4cxx-windows-static.yml
b/.github/workflows/log4cxx-windows-static.yml
index 124b57cd..fc7c4af6 100644
--- a/.github/workflows/log4cxx-windows-static.yml
+++ b/.github/workflows/log4cxx-windows-static.yml
@@ -75,7 +75,15 @@ jobs:
cd main
mkdir build
cd build
- cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
-DBUILD_SHARED_LIBS=off -DLOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER=on
-DLOG4CXX_TEST_PROGRAM_PATH=C:\msys64\usr\bin
"-DCMAKE_TOOLCHAIN_FILE=$THISDIR/vcpkg/scripts/buildsystems/vcpkg.cmake" ..
+ cmake `
+ -DLOG4CXX_TEST_ONLY_BUILD=1 `
+ -DVCPKG_TARGET_TRIPLET=x64-windows-static `
+ -DBUILD_SHARED_LIBS=off `
+ -DLOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER=on `
+ -DLOG4CXX_TEST_PROGRAM_PATH=C:\msys64\usr\bin `
+
"-DCMAKE_TOOLCHAIN_FILE=$THISDIR/vcpkg/scripts/buildsystems/vcpkg.cmake" `
+ ..
+
cmake --build .
- name: run unit tests
diff --git a/.github/workflows/log4cxx-windows.yml
b/.github/workflows/log4cxx-windows.yml
index 67dc667a..a93dd718 100644
--- a/.github/workflows/log4cxx-windows.yml
+++ b/.github/workflows/log4cxx-windows.yml
@@ -77,6 +77,7 @@ jobs:
$ROOT=Get-Location
Set-PSDebug -Trace 1
cmake `
+ -DLOG4CXX_TEST_ONLY_BUILD=1 `
-DLOG4CXX_CHAR=wchar_t `
-DLOG4CXX_ENABLE_ODBC=on `
"-DLOG4CXX_QT_SUPPORT=${{ matrix.qt }}" `
diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
index 8a2526f4..25edd863 100644
--- a/src/main/cpp/CMakeLists.txt
+++ b/src/main/cpp/CMakeLists.txt
@@ -20,15 +20,25 @@ add_library(log4cxx)
if(${log4cxx_ABI_VER} GREATER 15)
set_target_properties(log4cxx PROPERTIES CXX_VISIBILITY_PRESET hidden)
endif()
+set(PRIVATE_COMPILE_DEFINITIONS)
+set(PUBLIC_COMPILE_DEFINITIONS)
if(BUILD_SHARED_LIBS)
- target_compile_definitions(log4cxx PRIVATE LOG4CXX)
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS LOG4CXX)
if(UNIX AND NOT APPLE)
# Make defined symbols non-preemptible, which can optimize relocation
processing
target_link_options(log4cxx PRIVATE "LINKER:-Bsymbolic")
endif()
else()
- target_compile_definitions(log4cxx PUBLIC LOG4CXX_STATIC)
+ list(APPEND PUBLIC_COMPILE_DEFINITIONS LOG4CXX_STATIC)
endif()
+if(LOG4CXX_TEST_ONLY_BUILD)
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS
"ENABLE_FAILING_APPENDER_SIMULATION_TESTING=1")
+endif()
+target_compile_definitions(log4cxx
+ PUBLIC ${PUBLIC_COMPILE_DEFINITIONS}
+ PRIVATE ${PRIVATE_COMPILE_DEFINITIONS}
+)
+
add_dependencies(log4cxx log4cxx-include)
set(extra_classes "")
diff --git a/src/main/cpp/appenderskeleton.cpp
b/src/main/cpp/appenderskeleton.cpp
index 12c083fd..e7deed4f 100644
--- a/src/main/cpp/appenderskeleton.cpp
+++ b/src/main/cpp/appenderskeleton.cpp
@@ -206,6 +206,13 @@ void AppenderSkeleton::setOption(const LogString& option,
{
setName(value);
}
+#if ENABLE_FAILING_APPENDER_SIMULATION_TESTING
+ else if (StringHelper::equalsIgnoreCase(option,
+ LOG4CXX_STR("FAILONMESSAGE"),
LOG4CXX_STR("failonmessage")))
+ {
+ m_priv->exceptionTriggeringMessage = value;
+ }
+#endif
}
const spi::ErrorHandlerPtr AppenderSkeleton::getErrorHandler() const
diff --git a/src/main/cpp/asyncappender.cpp b/src/main/cpp/asyncappender.cpp
index 61715c8f..3ba0dd01 100644
--- a/src/main/cpp/asyncappender.cpp
+++ b/src/main/cpp/asyncappender.cpp
@@ -195,6 +195,47 @@ struct AsyncAppender::AsyncAppenderPriv : public
AppenderSkeleton::AppenderSkele
*/
std::thread dispatcher;
+ /**
+ * Used to determine when to restart dispatch thread.
+ */
+ bool dispatcherActive{ false };
+
+ /**
+ * Used to determine whether to restart dispatch thread.
+ */
+ int dispatcherStartCount{ 0 };
+
+ /**
+ * The function the dispatcher executes.
+ */
+ void dispatch(const LogString& appenderName);
+
+ /**
+ * Start dispatcher if not already running.
+ */
+ void checkDispatcher(const LogString& appenderName)
+ {
+ // Restart dispatcher if it has stopped by an exception in an
attached appender.
+ if (!this->dispatcherActive && this->dispatcher.joinable())
+ this->dispatcher.join();
+
+ if (!this->dispatcher.joinable() && this->dispatcherStartCount
<= 1)
+ {
+ std::lock_guard<std::recursive_mutex> lock(this->mutex);
+ if (!this->dispatcher.joinable())
+ {
+ ++this->dispatcherStartCount;
+ this->dispatcherActive = true;
+ this->dispatcher =
ThreadUtility::instance()->createThread
+ ( LOG4CXX_STR("AsyncAppender")
+ ,
&AsyncAppender::AsyncAppenderPriv::dispatch
+ , this
+ , appenderName
+ );
+ }
+ }
+ }
+
void stopDispatcher()
{
bufferNotEmpty.notify_all();
@@ -315,12 +356,8 @@ void AsyncAppender::append(const spi::LoggingEventPtr&
event, Pool& p)
// Get a copy of this thread's diagnostic context
event->LoadDC();
- if (!priv->dispatcher.joinable())
- {
- std::lock_guard<std::recursive_mutex> lock(priv->mutex);
- if (!priv->dispatcher.joinable())
- priv->dispatcher =
ThreadUtility::instance()->createThread( LOG4CXX_STR("AsyncAppender"),
&AsyncAppender::dispatch, this );
- }
+ priv->checkDispatcher(getName());
+
if (priv->dispatcher.get_id() == std::this_thread::get_id()) // From an
appender attached to this?
{
std::unique_lock<std::mutex> lock(priv->bufferMutex);
@@ -373,6 +410,7 @@ void AsyncAppender::append(const spi::LoggingEventPtr&
event, Pool& p)
++priv->blockedCount;
priv->bufferNotFull.wait(lock, [this]()
{
+ priv->checkDispatcher(getName());
return priv->eventCount - priv->dispatchedCount
< priv->bufferSize;
});
--priv->blockedCount;
@@ -574,95 +612,95 @@ DiscardSummary::createEvent(::LOG4CXX_NS::helpers::Pool&
p,
}
#endif
-
-void AsyncAppender::dispatch()
+void AsyncAppender::AsyncAppenderPriv::dispatch(const LogString& appenderName)
{
size_t discardCount = 0;
size_t iterationCount = 0;
size_t waitCount = 0;
- size_t blockedCount = 0;
- std::vector<size_t> pendingCountHistogram(priv->bufferSize, 0);
+ size_t producerBlockedCount = 0;
+ int failureCount = 0;
+ std::vector<size_t> pendingCountHistogram(this->bufferSize, 0);
bool isActive = true;
while (isActive)
{
Pool p;
LoggingEventList events;
- events.reserve(priv->bufferSize);
- for (int count = 0; count < 2 && priv->dispatchedCount ==
priv->commitCount; ++count)
+ events.reserve(this->bufferSize);
+ for (int count = 0; count < 2 && this->dispatchedCount ==
this->commitCount; ++count)
std::this_thread::yield(); // Wait a bit
- if (priv->dispatchedCount == priv->commitCount)
+ if (this->dispatchedCount == this->commitCount)
{
++waitCount;
- std::unique_lock<std::mutex> lock(priv->bufferMutex);
- priv->bufferNotEmpty.wait(lock, [this]() -> bool
- { return 0 < priv->blockedCount ||
priv->dispatchedCount != priv->commitCount || priv->closed; }
+ std::unique_lock<std::mutex> lock(this->bufferMutex);
+ this->bufferNotEmpty.wait(lock, [this]() -> bool
+ { return 0 < this->blockedCount ||
this->dispatchedCount != this->commitCount || this->closed; }
);
}
- isActive = !priv->isClosed();
+ isActive = !this->isClosed();
- while (events.size() < priv->bufferSize &&
priv->dispatchedCount != priv->commitCount)
+ while (events.size() < this->bufferSize &&
this->dispatchedCount != this->commitCount)
{
- auto index = priv->dispatchedCount %
priv->buffer.size();
- const auto& data = priv->buffer[index];
+ auto index = this->dispatchedCount %
this->buffer.size();
+ const auto& data = this->buffer[index];
events.push_back(data.event);
if (data.pendingCount < pendingCountHistogram.size())
++pendingCountHistogram[data.pendingCount];
- ++priv->dispatchedCount;
+ ++this->dispatchedCount;
}
- priv->bufferNotFull.notify_all();
+ this->bufferNotFull.notify_all();
{
- std::lock_guard<std::mutex> lock(priv->bufferMutex);
- blockedCount += priv->blockedCount;
- for (auto& discardItem : priv->discardMap)
+ std::lock_guard<std::mutex> lock(this->bufferMutex);
+ producerBlockedCount += this->blockedCount;
+ for (auto& discardItem : this->discardMap)
{
events.push_back(discardItem.second.createEvent(p));
discardCount += discardItem.second.getCount();
}
- priv->discardMap.clear();
+ this->discardMap.clear();
}
for (auto item : events)
{
try
{
- priv->appenders.appendLoopOnAppenders(item, p);
+ this->appenders.appendLoopOnAppenders(item, p);
}
catch (std::exception& ex)
{
- if (!priv->isClosed())
- {
-
priv->errorHandler->error(LOG4CXX_STR("async dispatcher"), ex, 0, item);
- isActive = false;
- }
+ if (1 < ++failureCount)
+ break;
+ if (!this->isClosed())
+
this->errorHandler->error(LOG4CXX_STR("[") + appenderName + LOG4CXX_STR("]
AsyncAppender"), ex, spi::ErrorCode::WRITE_FAILURE, item);
}
catch (...)
{
- if (!priv->isClosed())
- {
-
priv->errorHandler->error(LOG4CXX_STR("async dispatcher"));
- isActive = false;
- }
+ if (1 < ++failureCount)
+ break;
+ if (!this->isClosed())
+
this->errorHandler->error(LOG4CXX_STR("[") + appenderName + LOG4CXX_STR("]
AsyncAppender unknown exception thrown"));
}
}
++iterationCount;
+ if (1 < failureCount)
+ break;
}
if (LogLog::isDebugEnabled())
{
Pool p;
- LogString msg(LOG4CXX_STR("[") + getName() + LOG4CXX_STR("]
AsyncAppender"));
+ LogString msg(LOG4CXX_STR("[") + appenderName + LOG4CXX_STR("]
AsyncAppender"));
#ifdef _DEBUG
msg += LOG4CXX_STR(" iterationCount ");
StringHelper::toString(iterationCount, p, msg);
msg += LOG4CXX_STR(" waitCount ");
StringHelper::toString(waitCount, p, msg);
- msg += LOG4CXX_STR(" blockedCount ");
- StringHelper::toString(blockedCount, p, msg);
+ msg += LOG4CXX_STR(" producerBlockedCount ");
+ StringHelper::toString(producerBlockedCount, p, msg);
msg += LOG4CXX_STR(" commitCount ");
- StringHelper::toString(priv->commitCount, p, msg);
+ StringHelper::toString(this->commitCount, p, msg);
#endif
msg += LOG4CXX_STR(" dispatchedCount ");
- StringHelper::toString(priv->dispatchedCount, p, msg);
+ StringHelper::toString(this->dispatchedCount, p, msg);
msg += LOG4CXX_STR(" discardCount ");
StringHelper::toString(discardCount, p, msg);
msg += LOG4CXX_STR(" pendingCountHistogram");
@@ -673,5 +711,7 @@ void AsyncAppender::dispatch()
}
LogLog::debug(msg);
}
-
+ this->dispatcherActive = false;
+ if (0 < this->blockedCount) // Restart this dispatcher?
+ this->bufferNotFull.notify_all();
}
diff --git a/src/main/cpp/fallbackerrorhandler.cpp
b/src/main/cpp/fallbackerrorhandler.cpp
index 9e364faf..e526e36b 100644
--- a/src/main/cpp/fallbackerrorhandler.cpp
+++ b/src/main/cpp/fallbackerrorhandler.cpp
@@ -67,19 +67,36 @@ void FallbackErrorHandler::setLogger(const LoggerPtr&
logger)
m_priv->appenderHolders.emplace(logger->getName(), logger);
}
-void FallbackErrorHandler::error(const LogString& message,
- const std::exception& e,
- int errorCode) const
+void FallbackErrorHandler::error(const LogString& message) const
{
- error(message, e, errorCode, 0);
+ LogLog::warn(message);
+ m_priv->errorReported = true;
+}
+
+void FallbackErrorHandler::error
+ ( const LogString& message
+ , const std::exception& ex
+ , int errorCode
+ ) const
+{
+ error(message, ex, errorCode, 0);
}
-void FallbackErrorHandler::error(const LogString& message,
- const std::exception& e,
- int, const spi::LoggingEventPtr&) const
+void FallbackErrorHandler::error
+ ( const LogString& message
+ , const std::exception& ex
+ , int errorCode
+ , const spi::LoggingEventPtr& event
+ ) const
{
+ Pool p;
if (LogLog::isDebugEnabled())
- LogLog::debug(LOG4CXX_STR("FB: The following error reported: ")
+ message, e);
+ {
+ LogString msg{ LOG4CXX_STR("FB: error code ") };
+ StringHelper::toString(errorCode, p, msg);
+ LogLog::debug(msg);
+ }
+ LogLog::warn(message, ex);
AppenderPtr primaryLocked = m_priv->primary.lock();
AppenderPtr backupLocked = m_priv->backup.lock();
@@ -119,6 +136,8 @@ void FallbackErrorHandler::error(const LogString& message,
}
}
m_priv->errorReported = true;
+ if (event)
+ backupLocked->doAppend(event, p);
}
void FallbackErrorHandler::setAppender(const AppenderPtr& primary1)
diff --git a/src/main/cpp/writerappender.cpp b/src/main/cpp/writerappender.cpp
index 2f9a8fce..3606c0e9 100644
--- a/src/main/cpp/writerappender.cpp
+++ b/src/main/cpp/writerappender.cpp
@@ -226,6 +226,10 @@ void WriterAppender::setEncoding(const LogString& enc)
void WriterAppender::subAppend(const spi::LoggingEventPtr& event, Pool& p)
{
+#if ENABLE_FAILING_APPENDER_SIMULATION_TESTING
+ if (event->getRenderedMessage() == _priv->exceptionTriggeringMessage)
+ throw RuntimeException(LOG4CXX_STR("Simulated fault"));
+#endif
LogString msg;
_priv->layout->format(msg, event, p);
diff --git a/src/main/include/log4cxx/asyncappender.h
b/src/main/include/log4cxx/asyncappender.h
index 6e3212fe..fe9ff01b 100644
--- a/src/main/include/log4cxx/asyncappender.h
+++ b/src/main/include/log4cxx/asyncappender.h
@@ -251,11 +251,6 @@ class LOG4CXX_EXPORT AsyncAppender :
AsyncAppender(const AsyncAppender&);
AsyncAppender& operator=(const AsyncAppender&);
- /**
- * Dispatch routine.
- */
- void dispatch();
-
}; // class AsyncAppender
LOG4CXX_PTR_DEF(AsyncAppender);
} // namespace log4cxx
diff --git a/src/main/include/log4cxx/private/appenderskeleton_priv.h
b/src/main/include/log4cxx/private/appenderskeleton_priv.h
index 6c512b42..8afaed7e 100644
--- a/src/main/include/log4cxx/private/appenderskeleton_priv.h
+++ b/src/main/include/log4cxx/private/appenderskeleton_priv.h
@@ -81,6 +81,10 @@ struct AppenderSkeleton::AppenderSkeletonPrivate
@returns true if the appender was not already closed
*/
bool setClosed();
+
+#if ENABLE_FAILING_APPENDER_SIMULATION_TESTING
+ LogString exceptionTriggeringMessage;
+#endif
};
}
diff --git a/src/main/include/log4cxx/varia/fallbackerrorhandler.h
b/src/main/include/log4cxx/varia/fallbackerrorhandler.h
index 5c218831..3fcf9f0d 100644
--- a/src/main/include/log4cxx/varia/fallbackerrorhandler.h
+++ b/src/main/include/log4cxx/varia/fallbackerrorhandler.h
@@ -99,7 +99,7 @@ class LOG4CXX_EXPORT FallbackErrorHandler :
Print a the error message passed as parameter on
<code>System.err</code>.
*/
- void error(const LogString& /* message */) const override {}
+ void error(const LogString& message) const override;
/**
The appender to which this error handler is attached.
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 33d132f4..ee114340 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -121,6 +121,9 @@ elseif(CMAKE_BUILD_TYPE)
else()
set(TEST_COMPILE_DEFINITIONS _DEBUG)
endif()
+if(LOG4CXX_TEST_ONLY_BUILD)
+ list(APPEND TEST_COMPILE_DEFINITIONS
"ENABLE_FAILING_APPENDER_SIMULATION_TESTING=1")
+endif()
get_filename_component(UNIT_TEST_WORKING_DIR ../resources ABSOLUTE)
if(LOG4CXX_CFSTRING)
diff --git a/src/test/cpp/asyncappendertestcase.cpp
b/src/test/cpp/asyncappendertestcase.cpp
index 276d6705..1d562dac 100644
--- a/src/test/cpp/asyncappendertestcase.cpp
+++ b/src/test/cpp/asyncappendertestcase.cpp
@@ -34,9 +34,11 @@
#include <log4cxx/spi/location/locationinfo.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
+#include <log4cxx/fileappender.h>
#include <log4cxx/file.h>
#include <ostream>
#include <thread>
+#include <fstream>
#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
#include <fmt/core.h>
@@ -166,6 +168,9 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
LOGUNIT_TEST(testXMLConfiguration);
LOGUNIT_TEST(testAsyncLoggerXML);
LOGUNIT_TEST(testRecursiveConfiguration);
+#endif
+#if ENABLE_FAILING_APPENDER_SIMULATION_TESTING
+ LOGUNIT_TEST(testAsyncAppenderFallback);
#endif
LOGUNIT_TEST(testAsyncLoggerProperties);
#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
@@ -308,11 +313,13 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
const std::vector<spi::LoggingEventPtr>& v =
vectorAppender->getVector();
LOGUNIT_ASSERT_EQUAL(LEN, v.size());
Pool p;
- for (size_t i = 0; i < LEN; i++)
+ int i{ 0 };
+ for (auto e : v)
{
LogString m(LOG4CXX_STR("message"));
StringHelper::toString(i, p, m);
- LOGUNIT_ASSERT(v[i]->getRenderedMessage() == m);
+ LOGUNIT_ASSERT_EQUAL(m,
e->getRenderedMessage());
+ ++i;
}
LOGUNIT_ASSERT_EQUAL(true, vectorAppender->isClosed());
}
@@ -657,8 +664,11 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
if (auto async2 =
LOG4CXX_NS::cast<AsyncAppender>(attachedAppender))
{
for (auto appender :
async2->getAllAppenders())
- if (vectorAppender =
LOG4CXX_NS::cast<VectorAppender>(appender))
+ {
+ vectorAppender =
LOG4CXX_NS::cast<VectorAppender>(appender);
+ if (vectorAppender)
break;
+ }
}
}
LOGUNIT_ASSERT(vectorAppender);
@@ -679,6 +689,76 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
}
#endif
+#if ENABLE_FAILING_APPENDER_SIMULATION_TESTING
+ void testAsyncAppenderFallback()
+ {
+ // Configure Log4cxx
+ auto status =
xml::DOMConfigurator::configure("input/xml/asyncWithFallback.xml");
+ LOGUNIT_ASSERT_EQUAL(status,
spi::ConfigurationStatus::Configured);
+
+ // Check configuration is as expected
+ auto root = Logger::getRootLogger();
+ auto appenders = root->getAllAppenders();
+ LOGUNIT_ASSERT_EQUAL(1, int(appenders.size()));
+ auto asyncAppender =
LOG4CXX_NS::cast<AsyncAppender>(appenders.front());
+ LOGUNIT_ASSERT(asyncAppender);
+ auto asyncAppenders = asyncAppender->getAllAppenders();
+ LOGUNIT_ASSERT_EQUAL(1, int(asyncAppenders.size()));
+ auto primaryAppender =
LOG4CXX_NS::cast<FileAppender>(asyncAppenders.front());
+ LOGUNIT_ASSERT(primaryAppender);
+ auto primaryFileName = primaryAppender->getFile();
+ LOGUNIT_ASSERT(11 < primaryFileName.size());
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("Primary.log"),
primaryFileName.substr(primaryFileName.size() - 11));
+
+ // Log some messages
+ size_t LEN = 2000;
+ for (size_t i = 0; i < LEN; i++)
+ {
+ LOG4CXX_INFO_ASYNC(root, "message " << i);
+ }
+ std::this_thread::sleep_for( std::chrono::milliseconds(
100 ) );
+ asyncAppenders = asyncAppender->getAllAppenders();
+ LOGUNIT_ASSERT_EQUAL(1, int(asyncAppenders.size()));
+ auto backupAppender =
LOG4CXX_NS::cast<FileAppender>(asyncAppenders.front());
+ LOGUNIT_ASSERT(backupAppender);
+ auto backupFileName = backupAppender->getFile();
+ LOGUNIT_ASSERT(10 < backupFileName.size());
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("Backup.log"),
backupFileName.substr(backupFileName.size() - 10));
+
+ // Check all messages were written
+ std::vector<int> messageCount;
+ for (auto const& filePathLS : {primaryFileName,
backupFileName})
+ {
+ LOG4CXX_ENCODE_CHAR(filePath, filePathLS);
+ std::ifstream input(filePath);
+ for (std::string line; std::getline(input,
line);)
+ {
+ auto pos = line.rfind(' ');
+ if (line.npos != pos && pos + 1 <
line.size())
+ {
+ try
+ {
+ auto msgNumber =
std::stoull(line.substr(pos));
+ if (messageCount.size()
<= msgNumber)
+
messageCount.resize(msgNumber + 1);
+
++messageCount[msgNumber];
+ }
+ catch (std::exception const& ex)
+ {
+ LogString msg;
+
helpers::Transcoder::decode(ex.what(), msg);
+ msg += LOG4CXX_STR("
processing\n");
+
helpers::Transcoder::decode(line, msg);
+
helpers::LogLog::warn(msg);
+ }
+ }
+ }
+ }
+ for (auto& count : messageCount)
+ LOGUNIT_ASSERT_EQUAL(1, count);
+ }
+#endif
+
void testAsyncLoggerProperties()
{
// Configure Log4cxx
diff --git a/src/test/resources/input/xml/asyncWithFallback.xml
b/src/test/resources/input/xml/asyncWithFallback.xml
new file mode 100644
index 00000000..6d139d28
--- /dev/null
+++ b/src/test/resources/input/xml/asyncWithFallback.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="Primary" class="FileAppender">
+ <param name="File"
value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}-Primary.log"
/>
+ <param name="Append" value="false"/>
+ <param name="FailOnMessage" value="message 1000"/>
+ <layout class="PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p %c{2} - %m%n"/>
+ </layout>
+ </appender>
+ <appender name="Backup" class="FileAppender">
+ <param name="File"
value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}-Backup.log" />
+ <param name="Append" value="false"/>
+ <layout class="PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p %c{2} - %m%n"/>
+ </layout>
+ </appender>
+ <appender name="ASYNC" class="AsyncAppender">
+ <appender-ref ref="Primary" fallback-ref="Backup"/>
+ </appender>
+ <root>
+ <priority value ="INFO" />
+ <appender-ref ref="ASYNC" />
+ </root>
+</log4j:configuration>