This is an automated email from the ASF dual-hosted git repository. swebb2066 pushed a commit to branch fallback_appender_configuration in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit cd63c8f50a5ad64fde2b331e7a4bf6d23040b777 Author: Stephen Webb <[email protected]> AuthorDate: Thu Mar 12 10:58:13 2026 +1100 Add 'fallback-ref' attribute support to the 'appender-ref' element of a configuration file --- 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..5968222a --- /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="${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="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)
