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 54f063c0 Add 'fallback-ref' attribute support to the 'appender-ref' 
element of a configuration file (#606)
54f063c0 is described below

commit 54f063c0752ab80a5d62f9570fb1967ecb2d4d22
Author: Stephen Webb <[email protected]>
AuthorDate: Fri Mar 13 10:22:49 2026 +1100

    Add 'fallback-ref' attribute support to the 'appender-ref' element of a 
configuration file (#606)
---
 src/examples/config/async-example.xml              |  7 ++-
 src/examples/config/async-fall-back-example.xml    | 38 ++++++++++++++++
 src/main/cpp/domconfigurator.cpp                   | 48 +++++++++++++++++---
 src/main/cpp/fallbackerrorhandler.cpp              | 52 +++++++++++++++-------
 src/main/include/log4cxx/appenderskeleton.h        |  2 +
 src/main/include/log4cxx/spi/errorhandler.h        | 11 ++++-
 .../include/log4cxx/varia/fallbackerrorhandler.h   | 16 +++++--
 src/site/markdown/concepts.md                      |  4 ++
 8 files changed, 147 insertions(+), 31 deletions(-)

diff --git a/src/examples/config/async-example.xml 
b/src/examples/config/async-example.xml
index 07a9357d..e56ef2ba 100644
--- a/src/examples/config/async-example.xml
+++ b/src/examples/config/async-example.xml
@@ -1,16 +1,15 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/";>
   <appender name="A1" class="RollingFileAppender">
-    <param name="File"   value="${TEMP}/SomeApplicationName.log" />
-    <param name="Append" value="true" />
+    <param name="File" 
value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.log" />
     <layout class="PatternLayout">
       <param name="ConversionPattern" value="%d %-5p %c{2} - %m%n"/>
     </layout>
   </appender>
   <appender name="SENDMAIL" class="SMTPAppender">
-    <param name="from"   value="[email protected]" />
+    <param name="from" value="${PROGRAM_FILE_PATH.STEM}@example.org" />
     <param name="to" value="[email protected]" />
-    <param name="subject" value="Service error detected" />
+    <param name="subject" value="Service error detected in 
${PROGRAM_FILE_PATH.STEM}" />
     <param name="SMTPHost" value="smtp.example.com"/>
     <layout class="PatternLayout">
                <param name="ConversionPattern" value="%-5p %c{2} - %m%n"/>
diff --git a/src/examples/config/async-fall-back-example.xml 
b/src/examples/config/async-fall-back-example.xml
new file mode 100644
index 00000000..5ab0c4af
--- /dev/null
+++ b/src/examples/config/async-fall-back-example.xml
@@ -0,0 +1,38 @@
+<?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="${LOG_SERVER_UNC_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="${LOG_SERVER_UNC_PATH}/${PROGRAM_FILE_PATH.STEM}.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="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}.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>
diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp
index 6e1ad59f..82b0d0d9 100644
--- a/src/main/cpp/domconfigurator.cpp
+++ b/src/main/cpp/domconfigurator.cpp
@@ -27,7 +27,7 @@
 #include <log4cxx/helpers/loader.h>
 #include <log4cxx/helpers/optionconverter.h>
 #include <log4cxx/config/propertysetter.h>
-#include <log4cxx/spi/errorhandler.h>
+#include <log4cxx/varia/fallbackerrorhandler.h>
 #include <log4cxx/spi/loggerfactory.h>
 #if LOG4CXX_ABI_VERSION <= 15
 #include <log4cxx/defaultloggerfactory.h>
@@ -100,10 +100,14 @@ public: // ...structor
 public: // Methods
        AppenderPtr findAppenderByName(apr_xml_elem* elem, const LogString& 
appenderName);
 
-       AppenderPtr findAppenderByReference(apr_xml_elem* appenderRef);
+       AppenderPtr findAppenderByReference(apr_xml_elem* appenderRef, const 
char* optionalAttributeName = nullptr);
 
        AppenderPtr parseAppender(apr_xml_elem* appenderElement);
 
+       void parseFallbackAppender(apr_xml_elem* element, const LogString& 
holderName, const AppenderAttachablePtr& holder, const AppenderPtr& primary, 
const AppenderSkeletonPtr& aSkel);
+
+       void parseFallbackAppender(apr_xml_elem* element, const LoggerPtr& l, 
const AppenderSkeletonPtr& primary);
+
        void parseErrorHandler(apr_xml_elem* element, const AppenderPtr& 
appender);
 
        FilterStore parseFilters(apr_xml_elem* element);
@@ -194,6 +198,7 @@ IMPLEMENT_LOG4CXX_OBJECT(DOMConfigurator)
 #define FILTER_TAG "filter"
 #define ERROR_HANDLER_TAG "errorHandler"
 #define REF_ATTR "ref"
+#define FALLBACK_REF_ATTR "fallback-ref"
 #define ADDITIVITY_ATTR "additivity"
 #define ASYNCHRONOUS_ATTR "asynchronous"
 #define THRESHOLD_ATTR "threshold"
@@ -239,10 +244,12 @@ AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::findAppenderByName(apr_xml_
 /**
  Used internally to parse appenders by IDREF element.
 */
-AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::findAppenderByReference(apr_xml_elem* 
appenderRef)
+AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::findAppenderByReference(apr_xml_elem* 
appenderRef, const char* optionalAttributeName)
 {
        AppenderPtr appender;
-       LogString appenderName(subst(getAttribute(appenderRef, REF_ATTR)));
+       LogString appenderName = subst(getAttribute(appenderRef, 
optionalAttributeName ? optionalAttributeName : REF_ATTR));
+       if (optionalAttributeName && appenderName.empty())
+               return appender;
        if (appenderName.empty())
        {
                LogString msg(LOG4CXX_STR("["));
@@ -381,6 +388,8 @@ AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::parseAppender(apr_xml_elem*
                                                                + LOG4CXX_STR(" 
named [") + appender->getName() + LOG4CXX_STR("]"));
                                                }
                                                
aa->addAppender(delegateAppender);
+                                               if (auto appSkeleton = 
LOG4CXX_NS::cast<AppenderSkeleton>(appender))
+                                                       
parseFallbackAppender(currentElement, appender->getName(), aa, 
delegateAppender, appSkeleton);
                                        }
                                }
                                else
@@ -414,6 +423,30 @@ AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::parseAppender(apr_xml_elem*
        }
 }
 
+void 
DOMConfigurator::DOMConfiguratorPrivate::parseFallbackAppender(apr_xml_elem* 
element, const LogString& holderName, const AppenderAttachablePtr& holder, 
const AppenderPtr& primary, const AppenderSkeletonPtr& aSkel)
+{
+       if (auto fallbackAppender = findAppenderByReference(element, 
FALLBACK_REF_ATTR))
+       {
+               auto fb = std::make_shared<varia::FallbackErrorHandler>();
+               fb->setAppender(primary);
+               fb->setBackupAppender(fallbackAppender);
+               fb->addAppenderHolder(holderName, holder);
+               aSkel->setErrorHandler(fb);
+       }
+}
+
+void 
DOMConfigurator::DOMConfiguratorPrivate::parseFallbackAppender(apr_xml_elem* 
element, const LoggerPtr& l, const AppenderSkeletonPtr& primary)
+{
+       if (auto fallbackAppender = findAppenderByReference(element, 
FALLBACK_REF_ATTR))
+       {
+               auto fb = std::make_shared<varia::FallbackErrorHandler>();
+               fb->setAppender(primary);
+               fb->setBackupAppender(fallbackAppender);
+               fb->setLogger(l);
+               primary->setErrorHandler(fb);
+       }
+}
+
 /**
 Used internally to parse an {@link ErrorHandler} element.
 */
@@ -640,7 +673,12 @@ void 
DOMConfigurator::DOMConfiguratorPrivate::parseChildrenOfLoggerElement(apr_x
                                }
                                newappenders.push_back(appender);
                                if (async)
+                               {
                                        async->addAppender(appender);
+                                       parseFallbackAppender(currentElement, 
async->getName(), async, appender, async);
+                               }
+                               else if (auto appSkeleton = 
LOG4CXX_NS::cast<AppenderSkeleton>(appender))
+                                       parseFallbackAppender(currentElement, 
logger, appSkeleton);
                        }
                }
                else if (tagName == LEVEL_TAG)
@@ -1349,4 +1387,4 @@ LogString DOMConfigurator::getAttribute(
 { return LogString{}; }
 LogString DOMConfigurator::subst(const LogString& value)
 { return LogString{}; }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/main/cpp/fallbackerrorhandler.cpp 
b/src/main/cpp/fallbackerrorhandler.cpp
index 3d5f2bef..9e364faf 100644
--- a/src/main/cpp/fallbackerrorhandler.cpp
+++ b/src/main/cpp/fallbackerrorhandler.cpp
@@ -15,15 +15,15 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
-#include <log4cxx/appender.h>
-#include <log4cxx/logger.h>
 #include <log4cxx/varia/fallbackerrorhandler.h>
 #include <log4cxx/helpers/loglog.h>
 #include <log4cxx/helpers/stringhelper.h>
-#include <log4cxx/spi/loggingevent.h>
 #include <log4cxx/hierarchy.h>
 #include <log4cxx/logmanager.h>
+#if LOG4CXX_ABI_VERSION <= 15
+#include <log4cxx/logger.h>
+#include <log4cxx/asyncappender.h>
+#endif
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::helpers;
@@ -36,7 +36,7 @@ struct FallbackErrorHandler::FallbackErrorHandlerPrivate
 {
        AppenderWeakPtr backup;
        AppenderWeakPtr primary;
-       std::vector<LoggerPtr> loggers;
+       std::map<LogString, spi::AppenderAttachableWeakPtr> appenderHolders;
        bool errorReported = false;
 };
 
@@ -47,6 +47,16 @@ FallbackErrorHandler::FallbackErrorHandler()
 
 FallbackErrorHandler::~FallbackErrorHandler() {}
 
+void FallbackErrorHandler::addAppenderHolder(const LogString& name, const 
spi::AppenderAttachablePtr& clx)
+{
+       if (LogLog::isDebugEnabled())
+       {
+               LogLog::debug(((LogString) LOG4CXX_STR("FB: Adding appender 
holder ["))
+                       + name + LOG4CXX_STR("]."));
+       }
+       m_priv->appenderHolders.emplace(name, clx);
+}
+
 void FallbackErrorHandler::setLogger(const LoggerPtr& logger)
 {
        if (LogLog::isDebugEnabled())
@@ -54,7 +64,7 @@ void FallbackErrorHandler::setLogger(const LoggerPtr& logger)
                LogLog::debug(((LogString) LOG4CXX_STR("FB: Adding logger ["))
                        + logger->getName() + LOG4CXX_STR("]."));
        }
-       m_priv->loggers.push_back(logger);
+       m_priv->appenderHolders.emplace(logger->getName(), logger);
 }
 
 void FallbackErrorHandler::error(const LogString& message,
@@ -69,11 +79,7 @@ void FallbackErrorHandler::error(const LogString& message,
        int, const spi::LoggingEventPtr&) const
 {
        if (LogLog::isDebugEnabled())
-       {
-               LogLog::debug(((LogString) LOG4CXX_STR("FB: The following error 
reported: "))
-                       +  message, e);
-               LogLog::debug(LOG4CXX_STR("FB: INITIATING FALLBACK 
PROCEDURE."));
-       }
+               LogLog::debug(LOG4CXX_STR("FB: The following error reported: ") 
+ message, e);
 
        AppenderPtr primaryLocked = m_priv->primary.lock();
        AppenderPtr backupLocked = m_priv->backup.lock();
@@ -83,21 +89,33 @@ void FallbackErrorHandler::error(const LogString& message,
                return;
        }
 
-       for (LoggerPtr l : m_priv->loggers)
+       for (auto& item : m_priv->appenderHolders)
        {
+               auto holderLocked = item.second.lock();
+               if (!holderLocked)
+                       continue;
                if (LogLog::isDebugEnabled())
                {
                        LogLog::debug(LOG4CXX_STR("FB: Replacing [")
                                + primaryLocked->getName() + LOG4CXX_STR("] 
with [")
-                               + backupLocked->getName() + LOG4CXX_STR("] in 
logger [")
-                               + l->getName() + LOG4CXX_STR("]."));
+                               + backupLocked->getName() + LOG4CXX_STR("] in 
[")
+                               + item.first + LOG4CXX_STR("]."));
                }
-               if (!l->replaceAppender(primaryLocked, backupLocked))
+#if LOG4CXX_ABI_VERSION <= 15
+               bool ok{ false };
+               if (auto logger = LOG4CXX_NS::cast<Logger>(holderLocked))
+                       ok = logger->replaceAppender(primaryLocked, 
backupLocked);
+               else if (auto asyncAppender = 
LOG4CXX_NS::cast<AsyncAppender>(holderLocked))
+                       ok = asyncAppender->replaceAppender(primaryLocked, 
backupLocked);
+               if (!ok)
+#else
+               if (!holderLocked->replaceAppender(primaryLocked, backupLocked))
+#endif
                {
                        LogLog::debug(LOG4CXX_STR("FB: Failed to replace [")
                                + primaryLocked->getName() + LOG4CXX_STR("] 
with [")
-                               + backupLocked->getName() + LOG4CXX_STR("] in 
logger [")
-                               + l->getName() + LOG4CXX_STR("]."));
+                               + backupLocked->getName() + LOG4CXX_STR("] in 
[")
+                               + item.first + LOG4CXX_STR("]."));
                }
        }
        m_priv->errorReported = true;
diff --git a/src/main/include/log4cxx/appenderskeleton.h 
b/src/main/include/log4cxx/appenderskeleton.h
index 5bc1a7db..b63ca46e 100644
--- a/src/main/include/log4cxx/appenderskeleton.h
+++ b/src/main/include/log4cxx/appenderskeleton.h
@@ -189,6 +189,8 @@ class LOG4CXX_EXPORT AppenderSkeleton :
                void setThreshold(const LevelPtr& threshold);
 
 }; // class AppenderSkeleton
+
+LOG4CXX_PTR_DEF(AppenderSkeleton);
 }  // namespace log4cxx
 
 #endif //_LOG4CXX_APPENDER_SKELETON_H
diff --git a/src/main/include/log4cxx/spi/errorhandler.h 
b/src/main/include/log4cxx/spi/errorhandler.h
index 89683123..9eabd6e3 100644
--- a/src/main/include/log4cxx/spi/errorhandler.h
+++ b/src/main/include/log4cxx/spi/errorhandler.h
@@ -20,7 +20,7 @@
 
 #include <log4cxx/spi/optionhandler.h>
 #include <log4cxx/helpers/exception.h>
-#include <log4cxx/appender.h>
+#include <log4cxx/spi/appenderattachable.h>
 #include <log4cxx/spi/loggingevent.h>
 
 namespace LOG4CXX_NS
@@ -66,6 +66,15 @@ class LOG4CXX_EXPORT ErrorHandler : public virtual 
OptionHandler
 
                virtual ~ErrorHandler() {}
 
+#if 15 < LOG4CXX_ABI_VERSION
+               /**
+               Add \c clx to the list of collections to search for the failed 
appender.
+               @param name the collection name.
+               @param clx has a collection of appenders.
+               */
+               virtual void addAppenderHolder(const LogString& name, const 
AppenderAttachablePtr& clx) {};
+#endif
+
                /**
                Add a reference to a logger to which the failing appender might
                be attached to. The failing appender will be searched and
diff --git a/src/main/include/log4cxx/varia/fallbackerrorhandler.h 
b/src/main/include/log4cxx/varia/fallbackerrorhandler.h
index a2ac5b6b..5c218831 100644
--- a/src/main/include/log4cxx/varia/fallbackerrorhandler.h
+++ b/src/main/include/log4cxx/varia/fallbackerrorhandler.h
@@ -19,10 +19,9 @@
 #define _LOG4CXX_VARIA_FALLBACK_ERROR_HANDLER_H
 
 #include <log4cxx/spi/errorhandler.h>
-#include <log4cxx/helpers/object.h>
-#include <log4cxx/appender.h>
+#if LOG4CXX_ABI_VERSION <= 15
 #include <log4cxx/logger.h>
-#include <vector>
+#endif
 
 namespace LOG4CXX_NS
 {
@@ -36,6 +35,9 @@ whatever reason.
 
 <p>The error message is printed on <code>System.err</code>, and
 logged in the new secondary appender.
+
+Here is a sample configuration file that installs this error handler:
+\include async-fall-back-example.xml
 */
 class LOG4CXX_EXPORT FallbackErrorHandler :
        public virtual spi::ErrorHandler
@@ -53,13 +55,19 @@ class LOG4CXX_EXPORT FallbackErrorHandler :
                FallbackErrorHandler();
                ~FallbackErrorHandler();
 
+               /**
+               Add \c clx to the list of collections to search for the failed 
appender.
+               @param name used in log messages.
+               @param clx has a collection of appenders.
+               */
+               void addAppenderHolder(const LogString& name, const 
spi::AppenderAttachablePtr& clx) LOG4CXX_16_VIRTUAL_SPECIFIER;
+
                /**
                <em>Adds</em> the logger passed as parameter to the list of
                loggers that we need to search for in case of appender failure.
                */
                void setLogger(const LoggerPtr& logger) override;
 
-
                /**
                \copybrief spi::OptionHandler::activateOptions()
 
diff --git a/src/site/markdown/concepts.md b/src/site/markdown/concepts.md
index cb2b3284..5fc62f64 100644
--- a/src/site/markdown/concepts.md
+++ b/src/site/markdown/concepts.md
@@ -462,6 +462,10 @@ Log4cxx provides appenders to write to:
 The [addAppender](@ref log4cxx.Logger.addAppender)
 method adds an appender to a given logger.
 More than one appender can be attached to a logger.
+A backup appender can be attached to an appender
+(by using [FallbackErrorHandler](@ref log4cxx.varia.FallbackErrorHandler))
+to ensure continuation of output in the event that
+the primary destination can no longer be used.
 
 If the same file receives log requests concurrently from multiple process,
 use [this appender](@ref log4cxx.rolling.MultiprocessRollingFileAppender)

Reply via email to