This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch async_fmt_macros
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git

commit 8111f1148a25ec1f0c62beaf9995c658e517ceb7
Author: Stephen Webb <[email protected]>
AuthorDate: Sun Oct 12 16:14:11 2025 +1100

    Allow binary-to-text conversion to be moved to a background thread
---
 CMakeLists.txt                                 |   1 +
 src/examples/cpp/CMakeLists.txt                |  14 ++
 src/examples/cpp/format-async.cpp              |  80 +++++++++++
 src/main/cpp/asyncbuffer.cpp                   |  49 ++++++-
 src/main/cpp/basicconfigurator.cpp             |  61 +++++---
 src/main/include/CMakeLists.txt                |   1 +
 src/main/include/log4cxx/basicconfigurator.h   |  18 ++-
 src/main/include/log4cxx/helpers/asyncbuffer.h | 190 ++++++++++++++++++++++++-
 src/main/include/log4cxx/log4cxx.h.in          |   2 +
 src/site/doxy/Doxyfile.in                      |   3 +-
 src/test/cpp/benchmark/benchmark.cpp           |   2 +-
 11 files changed, 396 insertions(+), 25 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4bb3e1d..12ba8605 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -105,6 +105,7 @@ endif(LOG4CXX_ENABLE_ESMTP)
 find_package(fmt 7.1 QUIET)
 if(${fmt_FOUND})
     option(ENABLE_FMT_LAYOUT "Enable the FMT layout(if libfmt found)" ON)
+    option(ENABLE_FMT_ASYNC "Add support for the LOG4CXX_XXXX_FMT_ASYNC macros 
(if libfmt found)" ON)
 else()
     set(ENABLE_FMT_LAYOUT "OFF")
 endif()
diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index 983bb748..87dc2ec6 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -74,3 +74,17 @@ if(${fmt_FOUND})
         )
     endif()
 endif(${fmt_FOUND})
+
+if(ENABLE_FMT_ASYNC)
+    add_executable( format-async format-async.cpp )
+    target_compile_definitions(format-async PRIVATE 
${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS} 
${APR_UTIL_COMPILE_DEFINITIONS} )
+    target_include_directories(format-async PRIVATE ${CMAKE_CURRENT_LIST_DIR} 
$<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
+    target_link_libraries(format-async PRIVATE log4cxx ${APR_UTIL_LIBRARIES} 
${EXPAT_LIBRARIES} ${APR_LIBRARIES} ${APR_SYSTEM_LIBS} fmt::fmt)
+    if( WIN32 )
+        set_target_properties( format-async PROPERTIES
+          VS_DEBUGGER_ENVIRONMENT "PATH=${ESCAPED_PATH}"
+          VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+          FOLDER Examples
+        )
+    endif()
+endif(ENABLE_FMT_ASYNC)
diff --git a/src/examples/cpp/format-async.cpp 
b/src/examples/cpp/format-async.cpp
new file mode 100644
index 00000000..d9895568
--- /dev/null
+++ b/src/examples/cpp/format-async.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <log4cxx/logger.h>
+#include <log4cxx/logmanager.h>
+#include <log4cxx/basicconfigurator.h>
+#include <locale.h>
+#include <fmt/core.h>
+#include <fmt/args.h>
+#include <fmt/color.h>
+#include <fmt/ostream.h>
+#include <iomanip>
+
+struct MyStruct {
+               int x;
+};
+
+std::ostream& operator<<( std::ostream& stream, const MyStruct& mystruct ){
+               stream << "[MyStruct x:" << mystruct.x << "]";
+               return stream;
+}
+
+#if LOG4CXX_USING_STD_FORMAT
+template <typename Char>
+struct basic_ostream_formatter
+       : std::formatter<std::basic_string_view<Char>, Char>
+{
+       template <typename T, typename OutputIt>
+       auto format(const T& value, std::basic_format_context<OutputIt, Char>& 
ctx) const -> OutputIt
+       {
+               std::basic_stringstream<Char> ss;
+               ss << value;
+               return std::formatter<std::basic_string_view<Char>, 
Char>::format(ss.view(), ctx);
+       }
+};
+template <> struct std::formatter<MyStruct> : basic_ostream_formatter<char> {};
+#elif FMT_VERSION >= (9 * 10000)
+template <> struct fmt::formatter<MyStruct> : ostream_formatter {};
+#endif
+
+int main()
+{
+       setlocale(LC_ALL, "");
+
+       using namespace log4cxx;
+       BasicConfigurator::configureAsync();
+       auto rootLogger = Logger::getRootLogger();
+
+       LOG4CXX_INFO_FMT_ASYNC( rootLogger, "This is a {} mesage", "test" );
+       LOG4CXX_INFO_FMT_ASYNC( rootLogger, "We can also align text to the 
{:<10} or {:>10}", "left", "right" );
+
+       MyStruct mine{ 42 };
+       LOG4CXX_INFO_FMT_ASYNC( rootLogger, "This custom type {} can also be 
logged, since it implements operator<<", mine );
+
+       LOG4CXX_INFO_ASYNC( rootLogger, "Numbers can be formatted with 
excessive operator<<: "
+                                 << std::setprecision(3) << 22.456
+                                 << " And as hex: "
+                                 << std::setbase( 16 ) << 123 );
+       LOG4CXX_INFO_FMT_ASYNC( rootLogger, "Numbers can be formatted with a 
format string {:.1f} and as hex: {:x}", 22.456, 123 );
+       // Uncomment the next line to verify that compile time type checking 
works
+       //LOG4CXX_INFO_FMT_ASYNC( rootLogger, "Numbers can be formatted with a 
format string {:.1f} and as hex: {:x}", "wrong type", 123 );
+
+       LogManager::shutdown();
+       return 0;
+}
diff --git a/src/main/cpp/asyncbuffer.cpp b/src/main/cpp/asyncbuffer.cpp
index b7e5daf5..32d82cfb 100644
--- a/src/main/cpp/asyncbuffer.cpp
+++ b/src/main/cpp/asyncbuffer.cpp
@@ -26,8 +26,36 @@ namespace helpers
 struct AsyncBuffer::Private
 {
        std::vector<MessageBufferAppender> data;
+
+       Private(const MessageBufferAppender& f)
+               : data{ f }
+       {}
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+       fmt::string_view                                   fmt_string;
+       fmt::dynamic_format_arg_store<fmt::format_context> fmt_args;
+
+       Private(fmt::string_view&& format_string, 
fmt::dynamic_format_arg_store<fmt::format_context>&& args)
+               : fmt_string{ std::move(format_string) }
+               , fmt_args{ std::move(args) }
+       {}
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 };
 
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+void AsyncBuffer::initializeForFmt(fmt::string_view&& format_string, 
fmt::dynamic_format_arg_store<fmt::format_context>&& args)
+{
+       if (!m_priv)
+               m_priv = std::make_unique<Private>(std::move(format_string), 
std::move(args));
+       else
+       {
+               m_priv->fmt_string = std::move(format_string);
+               m_priv->fmt_args = std::move(args);
+       }
+}
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 /** An empty buffer.
 */
 AsyncBuffer::AsyncBuffer()
@@ -49,16 +77,28 @@ AsyncBuffer::~AsyncBuffer()
 /**
 * Has no item been added to this?
 */
-bool AsyncBuffer::empty() const { return !m_priv || m_priv->data.empty(); }
+bool AsyncBuffer::empty() const
+{
+       bool result{ true };
+       if (m_priv)
+               result = m_priv->data.empty() && 0 == m_priv->fmt_string.size();
+       return result;
+}
 
 /**
 * Add text version of buffered values to \c msg
 */
-void AsyncBuffer::renderMessage(LogCharMessageBuffer& msg)
+void AsyncBuffer::renderMessage(LogCharMessageBuffer& msg) const
 {
        if (m_priv)
+       {
                for (auto& renderer : m_priv->data)
                        renderer(msg);
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+               if (0 < m_priv->fmt_string.size())
+                       msg << fmt::vformat(m_priv->fmt_string, 
m_priv->fmt_args);
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+       }
 }
 
 /**
@@ -76,8 +116,9 @@ void AsyncBuffer::clear()
 void AsyncBuffer::append(const MessageBufferAppender& f)
 {
        if (!m_priv)
-               m_priv = std::make_unique<Private>();
-       m_priv->data.push_back(f);
+               m_priv = std::make_unique<Private>(f);
+       else
+               m_priv->data.push_back(f);
 }
 
 } // namespace helpers
diff --git a/src/main/cpp/basicconfigurator.cpp 
b/src/main/cpp/basicconfigurator.cpp
index e369f8ef..3895fa45 100644
--- a/src/main/cpp/basicconfigurator.cpp
+++ b/src/main/cpp/basicconfigurator.cpp
@@ -18,34 +18,63 @@
 #include <log4cxx/basicconfigurator.h>
 #include <log4cxx/patternlayout.h>
 #include <log4cxx/consoleappender.h>
+#include <log4cxx/asyncappender.h>
 #include <log4cxx/logmanager.h>
 #include <log4cxx/logger.h>
 #include <log4cxx/helpers/loglog.h>
 
 using namespace LOG4CXX_NS;
 
+namespace
+{
+
+LayoutPtr getDefaultLayout()
+{
+       auto pattern = LogString
+               { helpers::LogLog::isColorEnabled()
+               ? LOG4CXX_STR("%r [%t] %p %c %x - %Y%m%y%n")
+               : LOG4CXX_STR("%r [%t] %p %c %x - %m%n")
+               };
+       return std::make_shared<PatternLayout>(pattern);
+}
+
+} // namespace
+
+
 void BasicConfigurator::configure(const LayoutPtr& layoutArg)
 {
-       LogManager::getLoggerRepository()->setConfigured(true);
-       auto layout = layoutArg;
-       if (!layout)
-       {
-               auto pattern = LogString
-                       { helpers::LogLog::isColorEnabled()
-                       ? LOG4CXX_STR("%r [%t] %p %c %x - %Y%m%y%n")
-                       : LOG4CXX_STR("%r [%t] %p %c %x - %m%n")
-                       };
-               layout = std::make_shared<PatternLayout>(pattern);
-       }
-       auto appender = std::make_shared<ConsoleAppender>(layout);
-       Logger::getRootLogger()->addAppender(appender);
+       auto appender = std::make_shared<ConsoleAppender>(layoutArg ? layoutArg 
: getDefaultLayout());
+       appender->setName(LOG4CXX_STR("BasicConfigurator"));
+       auto r = LogManager::getLoggerRepository();
+       r->getRootLogger()->addAppender(appender);
+       r->setConfigured(true);
 }
 
 void BasicConfigurator::configure(const AppenderPtr& appender)
 {
-       LogManager::getLoggerRepository()->setConfigured(true);
-       LoggerPtr root = Logger::getRootLogger();
-       root->addAppender(appender);
+       auto r = LogManager::getLoggerRepository();
+       r->getRootLogger()->addAppender(appender);
+       r->setConfigured(true);
+}
+
+void BasicConfigurator::configureAsync(const LayoutPtr& layoutArg)
+{
+       auto ringBuffer = std::make_shared<AsyncAppender>();
+       ringBuffer->setName(LOG4CXX_STR("Default"));
+       ringBuffer->addAppender(std::make_shared<ConsoleAppender>(layoutArg ? 
layoutArg : getDefaultLayout()));
+       auto r = LogManager::getLoggerRepository();
+       r->getRootLogger()->addAppender(ringBuffer);
+       r->setConfigured(true);
+}
+
+void BasicConfigurator::configureAsync(const AppenderPtr& appender)
+{
+       auto ringBuffer = std::make_shared<AsyncAppender>();
+       ringBuffer->addAppender(appender);
+       ringBuffer->setName(LOG4CXX_STR("Default"));
+       auto r = LogManager::getLoggerRepository();
+       r->getRootLogger()->addAppender(ringBuffer);
+       r->setConfigured(true);
 }
 
 void BasicConfigurator::resetConfiguration()
diff --git a/src/main/include/CMakeLists.txt b/src/main/include/CMakeLists.txt
index 57195f51..c5d52f33 100644
--- a/src/main/include/CMakeLists.txt
+++ b/src/main/include/CMakeLists.txt
@@ -164,6 +164,7 @@ foreach(varName
   HAS_PTHREAD_SIGMASK
   HAS_PTHREAD_SETNAME
   HAS_PTHREAD_GETNAME
+  ENABLE_FMT_ASYNC
   )
   if(${varName} EQUAL 0)
     continue()
diff --git a/src/main/include/log4cxx/basicconfigurator.h 
b/src/main/include/log4cxx/basicconfigurator.h
index a2eb6fbd..b46a00e7 100644
--- a/src/main/include/log4cxx/basicconfigurator.h
+++ b/src/main/include/log4cxx/basicconfigurator.h
@@ -42,7 +42,7 @@ class LOG4CXX_EXPORT BasicConfigurator
 
        public:
                /**
-               Add a ConsoleAppender to the root logger that formats output 
using \c layout.
+               Add a ConsoleAppender that formats output using \c layout to 
the root logger.
 
                If \c layout is not provided,
                use a PatternLayout with the conversion pattern:
@@ -57,6 +57,22 @@ class LOG4CXX_EXPORT BasicConfigurator
                */
                static void configure(const AppenderPtr& appender);
 
+               /**
+               Add a ConsoleAppender that formats output using \c layout to an 
AsyncAppender attacted the root logger.
+
+               If \c layout is not provided,
+               use a PatternLayout with the conversion pattern:
+               - <code>%%r [%%t] %%p %%c %%x - %%Y%%m%%y%%n</code> if color is 
enabled (the default)
+               - <code>%%r [%%t] %%p %%c %%x - %%m%%n</code> if [color is 
disabled](disabling-color.html)
+               */
+               static void configureAsync(const LayoutPtr& layout = 
LayoutPtr());
+
+               /**
+               Add <code>appender</code> to an AsyncAppender attacted to the 
root logger.
+               @param appender The appender that asynchronously formats output.
+               */
+               static void configureAsync(const AppenderPtr& appender);
+
                /**
                Reset the default hierarchy to its defaut. It is equivalent to
                calling
diff --git a/src/main/include/log4cxx/helpers/asyncbuffer.h 
b/src/main/include/log4cxx/helpers/asyncbuffer.h
index ebc01a04..20296627 100644
--- a/src/main/include/log4cxx/helpers/asyncbuffer.h
+++ b/src/main/include/log4cxx/helpers/asyncbuffer.h
@@ -21,6 +21,9 @@
 #include <log4cxx/helpers/messagebuffer.h>
 #include <functional>
 #include <vector>
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+#include <fmt/args.h>
+#endif
 
 namespace LOG4CXX_NS
 {
@@ -35,7 +38,7 @@ namespace helpers
  */
 class LOG4CXX_EXPORT AsyncBuffer
 {
-public:
+public: // ...structors
        /** An empty buffer.
        */
        AsyncBuffer();
@@ -48,6 +51,7 @@ public:
        */
        ~AsyncBuffer();
 
+public: // Operators
        /** Append a function to this buffer that will convert \c value to text.
         *   @param value type must be copy-constructable
         *   @return this buffer.
@@ -77,6 +81,8 @@ public:
                return *this;
        }
 #endif
+
+public: // Accessors
        /**
        * Has no item been added to this?
        */
@@ -85,13 +91,24 @@ public:
        /**
        * Add text version of buffered values to \c msg
        */
-       void renderMessage(LogCharMessageBuffer& msg);
+       void renderMessage(LogCharMessageBuffer& msg) const;
 
+public: // Modifiers
        /**
        * Remove all message appenders
        */
        void clear();
 
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+       template <typename... Args>
+       void setMessage(fmt::format_string<Args...> fmt_str, Args&&... args)
+       {
+               auto store = 
fmt::dynamic_format_arg_store<fmt::format_context>();
+               ( store.push_back(std::forward<Args>(args)), ...);
+               initializeForFmt(std::move(fmt_str), std::move(store));
+       }
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 private:
        AsyncBuffer(const AsyncBuffer&) = delete;
        AsyncBuffer& operator=(const AsyncBuffer&) = delete;
@@ -103,6 +120,10 @@ private:
         *   Append \c function to this buffer.
         */
        void append(const MessageBufferAppender& f);
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+       void initializeForFmt(fmt::string_view&& format_string, 
fmt::dynamic_format_arg_store<fmt::format_context>&& args);
+#endif
 };
 
 } // namespace helpers
@@ -136,8 +157,36 @@ LOG4CXX_DEBUG_ASYNC(m_log, "AddMesh:"
                if 
(LOG4CXX_UNLIKELY(::LOG4CXX_NS::Logger::isDebugEnabledFor(logger))) {\
                        ::LOG4CXX_NS::helpers::AsyncBuffer buf; \
                        logger->addDebugEvent(std::move(buf << message), 
LOG4CXX_LOCATION); }} while (0)
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+Add a new logging event containing a message defined by \c fmt and 
<code>...</code> to attached appender(s) if \c logger is enabled for 
<code>DEBUG</code> events.
+
+\usage
+~~~{.cpp}
+LOG4CXX_DEBUG_FMT_ASYNC(m_log, "AddMesh: name {} type 0x{x} materialName {} 
visible? {d} at {} +/- {}"
+       , meshName
+       , traits.Type
+       , meshObject.GetMaterialName()
+       , traits.IsDefaultVisible
+       , obj->getBoundingBox().getCenter()
+       , obj->getBoundingBox().getHalfSize()
+       );
+~~~
+
+@param logger the logger to be used.
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+*/
+#define LOG4CXX_DEBUG_FMT_ASYNC(logger, fmt, ...) do { \
+               if 
(LOG4CXX_UNLIKELY(::LOG4CXX_NS::Logger::isDebugEnabledFor(logger))) {\
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addDebugEvent(std::move(buf), 
LOG4CXX_LOCATION); }} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
 #else
 #define LOG4CXX_DEBUG_ASYNC(logger, message)
+#define LOG4CXX_DEBUG_FMT_ASYNC(logger, message)
 #endif
 
 #if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 5000
@@ -156,8 +205,30 @@ Add a new logging event containing \c message to attached 
appender(s) if \c logg
                if 
(LOG4CXX_UNLIKELY(::LOG4CXX_NS::Logger::isTraceEnabledFor(logger))) {\
                        ::LOG4CXX_NS::helpers::AsyncBuffer buf; \
                        logger->addTraceEvent(std::move(buf << message), 
LOG4CXX_LOCATION); }} while (0)
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+Add a new logging event containing a message defined by \c fmt and 
<code>...</code> to attached appender(s) if \c logger is enabled for 
<code>TRACE</code> events.
+
+\usage
+~~~{.cpp}
+    LOG4CXX_TRACE_FMT_ASYNC(m_log, "AddVertex: at {} n {} {}", p, n, color);
+~~~
+
+@param logger the logger to be used.
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+*/
+#define LOG4CXX_TRACE_FMT_ASYNC(logger, fmt, ...) do { \
+               if 
(LOG4CXX_UNLIKELY(::LOG4CXX_NS::Logger::isTraceEnabledFor(logger))) {\
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addTraceEvent(std::move(buf), 
LOG4CXX_LOCATION); }} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 #else
 #define LOG4CXX_TRACE_ASYNC(logger, message)
+#define LOG4CXX_TRACE_FMT_ASYNC(logger, message)
 #endif
 
 #if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 20000
@@ -185,8 +256,33 @@ LOG4CXX_INFO_ASYNC(m_log, surface->GetName()
 
 #endif
 
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+Add a new logging event containing a message defined by \c fmt and 
<code>...</code> to attached appender(s) if \c logger is enabled for 
<code>INFO</code> events.
+
+\usage
+~~~{.cpp}
+LOG4CXX_INFO_FMT_ASYNC(m_log, "{} successfully planned {:.1f}% planned area 
{:.4f}m^2 unplanned area {:.4f}m^2 planned segments {:d} of {:d}"
+       , surface->GetName(), (plannedArea  / (plannedArea + unplannedArea)) * 
100.0
+       , plannedArea, unplannedArea
+       , surface->GetSegmentPlanCount(), surface->GetSegmentCount()
+       );
+~~~
+
+@param logger the logger to be used.
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+*/
+#define LOG4CXX_INFO_FMT_ASYNC(logger, fmt, ...) do { \
+               if (::LOG4CXX_NS::Logger::isInfoEnabledFor(logger)) {\
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addInfoEvent(std::move(buf), LOG4CXX_LOCATION); 
}} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 #else
 #define LOG4CXX_INFO_ASYNC(logger, message)
+#define LOG4CXX_INFO_FMT_ASYNC(logger, message)
 #endif
 
 #if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 30000
@@ -208,8 +304,33 @@ catch (const std::exception& ex)
                if (::LOG4CXX_NS::Logger::isWarnEnabledFor(logger)) {\
                        ::LOG4CXX_NS::helpers::AsyncBuffer buf; \
                        logger->addWarnEvent(std::move(buf << message), 
LOG4CXX_LOCATION); }} while (0)
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+Add a new logging event containing a message defined by \c fmt and 
<code>...</code> to attached appender(s) if \c logger is enabled for 
<code>WARN</code> events.
+
+\usage
+~~~{.cpp}
+catch (const std::exception& ex)
+{
+    LOG4CXX_WARN_FMT_ASYNC(m_log, "{}: in {}", ex.what(), 
m_task->GetParamFilePath());
+}
+~~~
+
+@param logger the logger to be used.
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+*/
+#define LOG4CXX_WARN_FMT_ASYNC(logger, fmt, ...) do { \
+               if (::LOG4CXX_NS::Logger::isWarnEnabledFor(logger)) {\
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addWarnEvent(std::move(buf), LOG4CXX_LOCATION); 
}} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 #else
 #define LOG4CXX_WARN_ASYNC(logger, message)
+#define LOG4CXX_WARN_FMT_ASYNC(logger, message)
 #endif
 
 #if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 40000
@@ -232,6 +353,29 @@ catch (std::exception& ex)
                        ::LOG4CXX_NS::helpers::AsyncBuffer buf; \
                        logger->addErrorEvent(std::move(buf << message), 
LOG4CXX_LOCATION); }} while (0)
 
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+Add a new logging event containing a message defined by \c fmt and 
<code>...</code> to attached appender(s) if \c logger is enabled for 
<code>ERROR</code> events.
+
+\usage
+~~~{.cpp}
+catch (std::exception& ex)
+{
+       LOG4CXX_ERROR_FMT_ASYNC(m_log, "{} in AddScanData", ex.what());
+}
+~~~
+
+@param logger the logger to be used.
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+*/
+#define LOG4CXX_ERROR_FMT_ASYNC(logger, fmt, ...) do { \
+               if (::LOG4CXX_NS::Logger::isErrorEnabledFor(logger)) {\
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addErrorEvent(std::move(buf), 
LOG4CXX_LOCATION); }} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 /**
 If \c condition is not true, add a new logging event containing \c message to 
attached appender(s) if \c logger is enabled for <code>ERROR</code> events.
 
@@ -245,9 +389,30 @@ If \c condition is not true, add a new logging event 
containing \c message to at
                        LOG4CXX_STACKTRACE \
                        logger->addErrorEvent(std::move(buf << message), 
LOG4CXX_LOCATION); }} while (0)
 
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+If \c condition is not true, add a new logging event containing
+a message defined by \c fmt and <code>...</code> to attached appender(s)
+if \c logger is enabled for <code>ERROR</code> events.
+
+@param logger the logger to be used.
+@param condition condition
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+*/
+#define LOG4CXX_ASSERT_FMT_ASYNC(logger, condition, fmt, ...) do { \
+               if (!(condition) && 
::LOG4CXX_NS::Logger::isErrorEnabledFor(logger)) {\
+                       LOG4CXX_STACKTRACE \
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addErrorEvent(std::move(buf), 
LOG4CXX_LOCATION); }} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 #else
 #define LOG4CXX_ERROR_ASYNC(logger, message)
+#define LOG4CXX_ERROR_FMT_ASYNC(logger, message)
 #define LOG4CXX_ASSERT_ASYNC(logger, condition, message)
+#define LOG4CXX_ASSERT_FMT_ASYNC(logger, condition, message)
 #endif
 
 #if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 50000
@@ -267,8 +432,29 @@ LOG4CXX_FATAL_ASYNC(m_log, m_renderSystem->getName() << " 
is not supported");
                        ::LOG4CXX_NS::helpers::AsyncBuffer buf; \
                        logger->addFatalEvent(std::move(buf << message), 
LOG4CXX_LOCATION); }} while (0)
 
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+/**
+Add a new logging event containing a message defined by \c fmt and 
<code>...</code> to attached appender(s) if \c logger is enabled for 
<code>FATAL</code> events.
+
+\usage
+~~~{.cpp}
+LOG4CXX_FATAL_FMT_ASYNC(m_log, "{} is not supported", 
m_renderSystem->getName());
+~~~
+@param logger the logger to be used.
+@param fmt the layout of the message.
+@param ... the variable parts of the message.
+
+*/
+#define LOG4CXX_FATAL_FMT_ASYNC(logger, fmt, ...) do { \
+               if (::LOG4CXX_NS::Logger::isFatalEnabledFor(logger)) {\
+                       ::LOG4CXX_NS::helpers::AsyncBuffer buf;\
+                       buf.setMessage(fmt LOG4CXX_FMT_VA_ARG(__VA_ARGS__));\
+                       logger->addFatalEvent(std::move(buf), 
LOG4CXX_LOCATION); }} while (0)
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
 #else
 #define LOG4CXX_FATAL_ASYNC(logger, message)
+#define LOG4CXX_FATAL_FMT_ASYNC(logger, message)
 #endif
 
 /**@} Logging macro group */
diff --git a/src/main/include/log4cxx/log4cxx.h.in 
b/src/main/include/log4cxx/log4cxx.h.in
index 0de00ecc..4e6c2931 100644
--- a/src/main/include/log4cxx/log4cxx.h.in
+++ b/src/main/include/log4cxx/log4cxx.h.in
@@ -131,8 +131,10 @@ namespace log4cxx = LOG4CXX_NS;
 #define LOG4CXX_USING_STD_FORMAT @LOG4CXX_USE_STANDARD_FORMAT@
 #if !defined(LOG4CXX_FORMAT_NS) && LOG4CXX_USING_STD_FORMAT
 #define LOG4CXX_FORMAT_NS std
+#define LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT 0
 #elif !defined(LOG4CXX_FORMAT_NS)
 #define LOG4CXX_FORMAT_NS fmt
+#define LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT @ENABLE_FMT_ASYNC@
 #endif
 
 #endif
diff --git a/src/site/doxy/Doxyfile.in b/src/site/doxy/Doxyfile.in
index 8de9efcb..009395ac 100644
--- a/src/site/doxy/Doxyfile.in
+++ b/src/site/doxy/Doxyfile.in
@@ -2350,7 +2350,8 @@ PREDEFINED             = LOG4CXX_NS=log4cxx \
                          LOG4CXX_UNICHAR_API \
                          LOG4CXX_CFSTRING_API \
                          LOG4CXX_ABI_VERSION=@log4cxx_ABI_VER@ \
-                         LOG4CXX_HAS_DOMCONFIGURATOR=1
+                         LOG4CXX_HAS_DOMCONFIGURATOR=1 \
+                                                
LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT=1
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
diff --git a/src/test/cpp/benchmark/benchmark.cpp 
b/src/test/cpp/benchmark/benchmark.cpp
index 85a348c2..0fd2b8c3 100644
--- a/src/test/cpp/benchmark/benchmark.cpp
+++ b/src/test/cpp/benchmark/benchmark.cpp
@@ -495,7 +495,7 @@ BENCHMARK_DEFINE_F(benchmarker, 
asyncIntPlus10FloatValueFmtBuffer)(benchmark::St
                , static_cast<float>(rand()) / static_cast<float>(RAND_MAX)
                , static_cast<float>(rand()) / static_cast<float>(RAND_MAX)
                };
-               LOG4CXX_INFO_FMT(m_asyncLogger, "Hello: msg number {} 
pseudo-random float {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} 
{:.3f} {:.3f}"
+               LOG4CXX_INFO_FMT_ASYNC(m_asyncLogger, "Hello: msg number {} 
pseudo-random float {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f} 
{:.3f} {:.3f}"
                        , ++x
                        , f[0]
                        , f[1]

Reply via email to