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)