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 44ad6d5a Provide a configuration file option to make a logger 
asynchronous (#550)
44ad6d5a is described below

commit 44ad6d5a679883e3aef3eef0ead85978ea4afb5e
Author: Stephen Webb <[email protected]>
AuthorDate: Mon Oct 6 13:11:57 2025 +1100

    Provide a configuration file option to make a logger asynchronous (#550)
    
    * Document the 'asynchronous' configuration file attribute
    
    * Add a change report entry
---
 src/main/cpp/appenderskeleton.cpp               |  5 ++
 src/main/cpp/asyncappender.cpp                  |  2 +-
 src/main/cpp/domconfigurator.cpp                | 30 +++++++++++-
 src/main/cpp/propertyconfigurator.cpp           | 34 ++++++++++---
 src/main/include/log4cxx/appenderskeleton.h     |  1 +
 src/main/include/log4cxx/asyncappender.h        | 11 ++++-
 src/site/markdown/change-report-gh.md           |  2 +
 src/site/markdown/configuration-samples.md      | 18 ++++++-
 src/test/cpp/asyncappendertestcase.cpp          | 65 ++++++++++++++++++++++++-
 src/test/resources/input/asyncLogger.properties | 20 ++++++++
 src/test/resources/input/xml/asyncLogger.xml    | 27 ++++++++++
 11 files changed, 199 insertions(+), 16 deletions(-)

diff --git a/src/main/cpp/appenderskeleton.cpp 
b/src/main/cpp/appenderskeleton.cpp
index f44b8ced..7f63cd0f 100644
--- a/src/main/cpp/appenderskeleton.cpp
+++ b/src/main/cpp/appenderskeleton.cpp
@@ -191,6 +191,11 @@ void AppenderSkeleton::setOption(const LogString& option,
        {
                setThreshold(Level::toLevelLS(value));
        }
+       else if (StringHelper::equalsIgnoreCase(option,
+                       LOG4CXX_STR("NAME"), LOG4CXX_STR("name")))
+       {
+               setName(value);
+       }
 }
 
 const spi::ErrorHandlerPtr AppenderSkeleton::getErrorHandler() const
diff --git a/src/main/cpp/asyncappender.cpp b/src/main/cpp/asyncappender.cpp
index d199e392..35ae409a 100644
--- a/src/main/cpp/asyncappender.cpp
+++ b/src/main/cpp/asyncappender.cpp
@@ -599,7 +599,7 @@ void AsyncAppender::dispatch()
        if (LogLog::isDebugEnabled())
        {
                Pool p;
-               LogString msg(LOG4CXX_STR("AsyncAppender"));
+               LogString msg(LOG4CXX_STR("[") + getName() + LOG4CXX_STR("] 
AsyncAppender"));
 #ifdef _DEBUG
                msg += LOG4CXX_STR(" iterationCount ");
                StringHelper::toString(iterationCount, p, msg);
diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp
index 7c940858..6983c9be 100644
--- a/src/main/cpp/domconfigurator.cpp
+++ b/src/main/cpp/domconfigurator.cpp
@@ -17,6 +17,7 @@
 #include <log4cxx/logstring.h>
 #include <log4cxx/xml/domconfigurator.h>
 #include <log4cxx/appender.h>
+#include <log4cxx/asyncappender.h>
 #include <log4cxx/layout.h>
 #include <log4cxx/logger.h>
 #include <log4cxx/logmanager.h>
@@ -132,6 +133,7 @@ IMPLEMENT_LOG4CXX_OBJECT(DOMConfigurator)
 #define ERROR_HANDLER_TAG "errorHandler"
 #define REF_ATTR "ref"
 #define ADDITIVITY_ATTR "additivity"
+#define ASYNCHRONOUS_ATTR "asynchronous"
 #define THRESHOLD_ATTR "threshold"
 #define STRINGSTREAM_ATTR "stringstream"
 #define CONFIG_DEBUG_ATTR "configDebug"
@@ -560,8 +562,18 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
        AppenderMap& appenders)
 {
        PropertySetter propSetter(logger);
-       std::vector<AppenderPtr> newappenders;
+       auto loggerName = m_priv->repository->getRootLogger() == logger
+                                       ? LogString(LOG4CXX_STR("root"))
+                                       : logger->getName();
+       AsyncAppenderPtr async;
+       auto lsAsynchronous = subst(getAttribute(utf8Decoder, loggerElement, 
ASYNCHRONOUS_ATTR));
+       if (!lsAsynchronous.empty() && 
OptionConverter::toBoolean(lsAsynchronous, true))
+       {
+               async = std::make_shared<AsyncAppender>();
+               async->setName(loggerName);
+       }
 
+       std::vector<AppenderPtr> newappenders;
        for (apr_xml_elem* currentElement = loggerElement->first_child;
                currentElement;
                currentElement = currentElement->next)
@@ -572,6 +584,8 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
                {
                        if (auto appender = findAppenderByReference(p, 
utf8Decoder, currentElement, doc, appenders))
                        {
+                               if (log4cxx::cast<AsyncAppender>(appender)) // 
An explicitly configured AsyncAppender?
+                                       async.reset(); // Not required
                                if (LogLog::isDebugEnabled())
                                {
                                        LogLog::debug(LOG4CXX_STR("Adding ") + 
Appender::getStaticClass().getName()
@@ -579,6 +593,8 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
                                                + LOG4CXX_STR(" to logger [") + 
logger->getName() + LOG4CXX_STR("]"));
                                }
                                newappenders.push_back(appender);
+                               if (async)
+                                       async->addAppender(appender);
                        }
                }
                else if (tagName == LEVEL_TAG)
@@ -594,7 +610,17 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
                        setParameter(p, utf8Decoder, currentElement, 
propSetter);
                }
        }
-       if (newappenders.empty())
+       if (async && !newappenders.empty())
+       {
+               if (LogLog::isDebugEnabled())
+               {
+                       LogLog::debug(LOG4CXX_STR("Asynchronous logging for [")
+                                       + logger->getName() + LOG4CXX_STR("] is 
on"));
+               }
+               logger->replaceAppenders({async});
+               m_priv->appenderAdded = true;
+       }
+       else if (newappenders.empty())
                logger->removeAllAppenders();
        else
        {
diff --git a/src/main/cpp/propertyconfigurator.cpp 
b/src/main/cpp/propertyconfigurator.cpp
index b1c1d939..9c1fdd24 100644
--- a/src/main/cpp/propertyconfigurator.cpp
+++ b/src/main/cpp/propertyconfigurator.cpp
@@ -17,6 +17,7 @@
 
 #include <log4cxx/logstring.h>
 #include <log4cxx/propertyconfigurator.h>
+#include <log4cxx/asyncappender.h>
 #include <log4cxx/helpers/properties.h>
 #include <log4cxx/helpers/loglog.h>
 #include <log4cxx/helpers/exception.h>
@@ -413,13 +414,18 @@ void PropertyConfigurator::parseLogger(
 
        }
 
-       AppenderPtr appender;
-       LogString appenderName;
-       std::vector<AppenderPtr> newappenders;
+       AsyncAppenderPtr async;
+       auto lsAsynchronous = 
OptionConverter::findAndSubst(LOG4CXX_STR("log4j.asynchronous.") + loggerName, 
props);
+       if (!lsAsynchronous.empty() && 
OptionConverter::toBoolean(lsAsynchronous, true))
+       {
+               async = std::make_shared<AsyncAppender>();
+               async->setName(loggerName);
+       }
 
+       std::vector<AppenderPtr> newappenders;
        while (st.hasMoreTokens())
        {
-               appenderName = StringHelper::trim(st.nextToken());
+               auto appenderName = StringHelper::trim(st.nextToken());
 
                if (appenderName.empty() || appenderName == LOG4CXX_STR(","))
                {
@@ -431,18 +437,30 @@ void PropertyConfigurator::parseLogger(
                        LogLog::debug(LOG4CXX_STR("Parsing ") + 
Appender::getStaticClass().getName()
                                + LOG4CXX_STR(" named [") + appenderName + 
LOG4CXX_STR("]"));
                }
-               appender = parseAppender(props, appenderName);
-
-               if (appender != 0)
+               if (auto appender = parseAppender(props, appenderName))
                {
                        newappenders.push_back(appender);
+                       if (log4cxx::cast<AsyncAppender>(appender)) // An 
explicitly configured AsyncAppender?
+                               async.reset(); // Not required
+                       if (async)
+                               async->addAppender(appender);
                }
        }
 #if 15 < LOG4CXX_ABI_VERSION
        if (!newappenders.empty())
                m_priv->appenderAdded = true;
 #endif
-       logger->reconfigure( newappenders, additivity );
+       if (async && !newappenders.empty())
+       {
+               if (LogLog::isDebugEnabled())
+               {
+                       LogLog::debug(LOG4CXX_STR("Asynchronous logging for [")
+                                       + loggerName + LOG4CXX_STR("] is on"));
+               }
+               logger->reconfigure( {async}, additivity );
+       }
+       else
+               logger->reconfigure( newappenders, additivity );
 }
 
 AppenderPtr PropertyConfigurator::parseAppender(
diff --git a/src/main/include/log4cxx/appenderskeleton.h 
b/src/main/include/log4cxx/appenderskeleton.h
index 3a47214d..30260371 100644
--- a/src/main/include/log4cxx/appenderskeleton.h
+++ b/src/main/include/log4cxx/appenderskeleton.h
@@ -92,6 +92,7 @@ class LOG4CXX_EXPORT AppenderSkeleton :
 
                Supported options | Supported values | Default value |
                -------------- | ---------------- | --------------- |
+               Name      | {any} | - |
                Threshold | Trace,Debug,Info,Warn,Error,Fatal,Off,All | All |
                */
                void setOption(const LogString& option, const LogString& value) 
override;
diff --git a/src/main/include/log4cxx/asyncappender.h 
b/src/main/include/log4cxx/asyncappender.h
index 406183d3..6e3212fe 100644
--- a/src/main/include/log4cxx/asyncappender.h
+++ b/src/main/include/log4cxx/asyncappender.h
@@ -34,14 +34,21 @@ The AsyncAppender stores the logging event in a bounded 
buffer
 and then returns control to the application.
 A separate thread forwards events to the attached appender(s).
 
+An AsyncAppender is used when you configure a logger to be asynchronous.
+These AsyncAppender(s) use [the default values](@ref 
log4cxx::AsyncAppender::setOption) for all options
+and they cannot be changed using configuration file entries.
+For more control over the AsyncAppender options,
+use <b>appender-ref</b> element in the logger configuration instead. 
+
 <b>Important notes:</b>
 - Your application must call LogManager::shutdown when it exits
 to prevent undefined behaviour when using this appender.
-- Runtime configuration requires an XML configuration file
+- Runtime configuration of options requires an XML configuration file
 (see the example below).
 
 This appender is useful when outputting to a slow event sink,
-for example, a remote SMTP server or a database.
+for example, unbuffered output to a file,
+a remote SMTP server or a database.
 Note that configuring a FileAppender to use [buffered output](@ref 
log4cxx::FileAppender::setOption)
 usually results in lower overhead than
 attaching the FileAppender to an AsyncAppender
diff --git a/src/site/markdown/change-report-gh.md 
b/src/site/markdown/change-report-gh.md
index 4b8b5354..7e4e4cca 100644
--- a/src/site/markdown/change-report-gh.md
+++ b/src/site/markdown/change-report-gh.md
@@ -63,6 +63,8 @@ and the LOG4CXX_CONFIGURATION environment variable (see 
log4cxx::spi::Configurat
    \[[#529](https://github.com/apache/logging-log4cxx/pull/529)\]
 * New logging macros that defer binary-to-text conversion until used in 
AsyncAppender's background thread
    \[[#548](https://github.com/apache/logging-log4cxx/pull/548)\]
+* A simplified way to attach an AsyncAppender to a logger using a 
configuration file
+   \[[#550](https://github.com/apache/logging-log4cxx/pull/550)\]
 
 The following issues have been addressed:
 
diff --git a/src/site/markdown/configuration-samples.md 
b/src/site/markdown/configuration-samples.md
index 416f9f99..6a83e15d 100644
--- a/src/site/markdown/configuration-samples.md
+++ b/src/site/markdown/configuration-samples.md
@@ -86,6 +86,21 @@ The variable names are [documented here](@ref 
log4cxx.spi.Configurator.propertie
 To check the correct values are used when your configuration file is loaded,
 use [Log4cxx internal debugging].
 
+# Configuring a logger to use asynchronous output
+
+Log4cxx 1.6 allows you to more easily attach an [AsyncAppender](@ref 
log4cxx.AsyncAppender)
+to a logger using a configuration file.
+- If using a properties file, add the line 
<code>log4j.asynchronous.{LOGGER_NAME}=true</code> to your file
+- If using an XML file, add the <code>asynchronous="true"</code> attribute in 
the <code>\<logger></code> element.
+
+The "asynchronous" attribute is only relevent for a logger with attached 
appenders.
+The attribute is ignored if the logger does not have any directly attached 
appenders.
+
+The "asynchronous" attribute results in the configured appender(s)
+being attached an [AsyncAppender](@ref log4cxx.AsyncAppender)
+and it is the [AsyncAppender](@ref log4cxx.AsyncAppender)
+that is attached directly to the logger.
+
 # Configuration Samples {#configuration-samples}
 
 The following snippets show various ways of configuring Log4cxx.
@@ -124,6 +139,7 @@ to store a log file per executable in a product related 
logs directory:
 ~~~{.properties}
 # Uncomment a line to enable debugging for a category
 log4j.rootCategory=INFO, A1
+log4j.asynchronous.root=true
 
 log4j.appender.A1=org.apache.log4j.RollingFileAppender
 log4j.appender.A1.MaxFileSize=5MB
@@ -286,7 +302,7 @@ to store a log file per executable in a product related 
logs directory:
     </layout>
   </appender>
 
-  <root>
+  <root asynchronous="true" >
      <priority value="info" />
      <appender-ref ref="ConsoleAppender"/>
      <appender-ref ref="FileAppender"/>
diff --git a/src/test/cpp/asyncappendertestcase.cpp 
b/src/test/cpp/asyncappendertestcase.cpp
index 9ba7e173..f4e5d909 100644
--- a/src/test/cpp/asyncappendertestcase.cpp
+++ b/src/test/cpp/asyncappendertestcase.cpp
@@ -32,6 +32,7 @@
 #include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/spi/location/locationinfo.h>
 #include <log4cxx/xml/domconfigurator.h>
+#include <log4cxx/propertyconfigurator.h>
 #include <log4cxx/file.h>
 #include <thread>
 
@@ -138,8 +139,10 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                LOGUNIT_TEST(testBufferOverflowBehavior);
                LOGUNIT_TEST(testLoggingAppender);
 #if LOG4CXX_HAS_DOMCONFIGURATOR
-               LOGUNIT_TEST(testConfiguration);
+               LOGUNIT_TEST(testXMLConfiguration);
+               LOGUNIT_TEST(testAsyncLoggerXML);
 #endif
+               LOGUNIT_TEST(testAsyncLoggerProperties);
                LOGUNIT_TEST_SUITE_END();
 
 #ifdef _DEBUG
@@ -531,7 +534,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                }
 
 #if LOG4CXX_HAS_DOMCONFIGURATOR
-               void testConfiguration()
+               void testXMLConfiguration()
                {
                        // Configure Log4cxx
                        auto status = 
xml::DOMConfigurator::configure("input/xml/asyncAppender1.xml");
@@ -562,8 +565,66 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        LOGUNIT_ASSERT_EQUAL(LEN, v.size());
                        LOGUNIT_ASSERT(vectorAppender->isClosed());
                }
+
+               void testAsyncLoggerXML()
+               {
+                       // Configure Log4cxx
+                       auto status = 
xml::DOMConfigurator::configure("input/xml/asyncLogger.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::cast<AsyncAppender>(appenders.front());
+                       LOGUNIT_ASSERT(asyncAppender);
+
+                       // Log some messages
+                       size_t LEN = 20;
+                       for (size_t i = 0; i < LEN; i++)
+                       {
+                               LOG4CXX_INFO_ASYNC(root, "message" << i);
+                       }
+                       asyncAppender->close();
+
+                       // Check all message were received
+                       auto vectorAppender = 
log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
+                       LOGUNIT_ASSERT(vectorAppender);
+                       auto& v = vectorAppender->getVector();
+                       LOGUNIT_ASSERT_EQUAL(LEN, v.size());
+                       LOGUNIT_ASSERT(vectorAppender->isClosed());
+               }
 #endif
 
+               void testAsyncLoggerProperties()
+               {
+                       // Configure Log4cxx
+                       auto status = 
PropertyConfigurator::configure("input/asyncLogger.properties");
+                       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::cast<AsyncAppender>(appenders.front());
+                       LOGUNIT_ASSERT(asyncAppender);
+
+                       // Log some messages
+                       size_t LEN = 20;
+                       for (size_t i = 0; i < LEN; i++)
+                       {
+                               LOG4CXX_INFO_ASYNC(root, "message" << i);
+                       }
+                       asyncAppender->close();
+
+                       // Check all message were received
+                       auto vectorAppender = 
log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
+                       LOGUNIT_ASSERT(vectorAppender);
+                       auto& v = vectorAppender->getVector();
+                       LOGUNIT_ASSERT_EQUAL(LEN, v.size());
+                       LOGUNIT_ASSERT(vectorAppender->isClosed());
+               }
+
 
 };
 
diff --git a/src/test/resources/input/asyncLogger.properties 
b/src/test/resources/input/asyncLogger.properties
new file mode 100644
index 00000000..6ec9ef71
--- /dev/null
+++ b/src/test/resources/input/asyncLogger.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+log4j.rootCategory=INFO, A1
+log4j.asynchronous.root=true
+
+log4j.appender.A1=org.apache.log4j.VectorAppender
+log4j.appender.A1.name=VECTOR
\ No newline at end of file
diff --git a/src/test/resources/input/xml/asyncLogger.xml 
b/src/test/resources/input/xml/asyncLogger.xml
new file mode 100644
index 00000000..cff50c18
--- /dev/null
+++ b/src/test/resources/input/xml/asyncLogger.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
+  <appender name="VECTOR" class="org.apache.log4j.VectorAppender"/>
+  <root asynchronous="true" >
+    <level value="INFO"/>
+    <appender-ref ref="VECTOR" />
+  </root>
+</log4j:configuration>

Reply via email to