This is an automated email from the ASF dual-hosted git repository. swebb2066 pushed a commit to branch async_fallback_testing in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit 392919244243c7071f463ee14472e30354c71a8c Author: Stephen Webb <[email protected]> AuthorDate: Fri Mar 13 10:37:45 2026 +1100 Ensure the AsyncAppender can fallback the appender on an exception --- .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 | 8 ++ src/main/cpp/asyncappender.cpp | 121 +++++++++++++-------- src/main/cpp/fallbackerrorhandler.cpp | 34 ++++-- 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 | 48 +++++++- src/test/resources/input/xml/asyncWithFallback.xml | 39 +++++++ 16 files changed, 234 insertions(+), 63 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..6131cd64 100644 --- a/src/main/cpp/appenderskeleton.cpp +++ b/src/main/cpp/appenderskeleton.cpp @@ -17,6 +17,7 @@ #include <log4cxx/spi/loggingevent.h> #include <log4cxx/appenderskeleton.h> +#include <log4cxx/helpers/exception.h> #include <log4cxx/helpers/loglog.h> #include <log4cxx/helpers/onlyonceerrorhandler.h> #include <log4cxx/level.h> @@ -206,6 +207,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..f2cf69be 100644 --- a/src/main/cpp/asyncappender.cpp +++ b/src/main/cpp/asyncappender.cpp @@ -195,6 +195,42 @@ 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() && 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 +351,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 +405,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 +607,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 +706,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..40309a58 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,7 @@ void FallbackErrorHandler::error(const LogString& message, } } m_priv->errorReported = true; + 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..2fae9a73 100644 --- a/src/test/cpp/asyncappendertestcase.cpp +++ b/src/test/cpp/asyncappendertestcase.cpp @@ -34,6 +34,7 @@ #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> @@ -166,6 +167,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 +312,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()); } @@ -679,6 +685,44 @@ 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)); + } +#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..663bd016 --- /dev/null +++ b/src/test/resources/input/xml/asyncWithFallback.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> + <appender name="Primary" class="RollingFileAppender"> + <rollingPolicy class="TimeBasedRollingPolicy"> + <param name="FileNamePattern" value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}-%d{yyyy-MM-dd-HH-mm-ss-SSS}.log"/> + </rollingPolicy> + <triggeringPolicy class="SizeBasedTriggeringPolicy"> + <param name="MaxFileSize" value="5MB"/> + </triggeringPolicy> + <param name="File" value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}-Primary.log" /> + <param name="BufferedIO" value="true"/> + <param name="BufferedSeconds" value="1"/> + <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="RollingFileAppender"> + <rollingPolicy class="TimeBasedRollingPolicy"> + <param name="FileNamePattern" value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}-%d{yyyy-MM-dd-HH-mm-ss-SSS}.log"/> + </rollingPolicy> + <triggeringPolicy class="SizeBasedTriggeringPolicy"> + <param name="MaxFileSize" value="5MB"/> + </triggeringPolicy> + <param name="File" value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}-Backup.log" /> + <param name="BufferedIO" value="true"/> + <param name="BufferedSeconds" value="1"/> + <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>
