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 08c83f5f Allow binary-to-text conversion to be moved to a background
thread (#554)
08c83f5f is described below
commit 08c83f5f9bd8ae000d71ee9a5d6e75a7f7307682
Author: Stephen Webb <[email protected]>
AuthorDate: Tue Oct 14 09:58:54 2025 +1100
Allow binary-to-text conversion to be moved to a background thread (#554)
* Update example benchmark data
* Allow all wide or all narrow strings in XXXXX_FMT_ASYNC macros
* Add a unit test
* Older fmt version fails so remove it from CI
* Include XXXXX_ASYNC macro support in the Log4cxx configuration summary
---
.github/workflows/log4cxx-ubuntu.yml | 4 +-
CMakeLists.txt | 4 +
src/examples/cpp/CMakeLists.txt | 14 ++
src/examples/cpp/format-async.cpp | 69 ++++++++
src/main/cpp/asyncbuffer.cpp | 110 ++++++++++++-
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 | 211 ++++++++++++++++++++++++-
src/main/include/log4cxx/log4cxx.h.in | 2 +
src/site/doxy/Doxyfile.in | 3 +-
src/site/markdown/change-report-gh.md | 3 +-
src/site/markdown/performance.md | 8 +-
src/test/cpp/CMakeLists.txt | 12 +-
src/test/cpp/asyncappendertestcase.cpp | 101 +++++++++++-
src/test/cpp/benchmark/benchmark.cpp | 10 +-
16 files changed, 591 insertions(+), 40 deletions(-)
diff --git a/.github/workflows/log4cxx-ubuntu.yml
b/.github/workflows/log4cxx-ubuntu.yml
index cd4b4863..6375fedf 100644
--- a/.github/workflows/log4cxx-ubuntu.yml
+++ b/.github/workflows/log4cxx-ubuntu.yml
@@ -42,7 +42,7 @@ jobs:
- name: ubuntu22-clang
os: ubuntu-22.04
cxx: clang++
- fmt: ON
+ fmt: OFF
qt: OFF
qt6: OFF
odbc: ON
@@ -55,7 +55,7 @@ jobs:
- name: ubuntu24-gcc
os: ubuntu-24.04
cxx: g++
- fmt: OFF
+ fmt: ON
qt: ON
qt6: ON
odbc: OFF
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4bb3e1d..7db4e1eb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -105,8 +105,10 @@ 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")
+ set(ENABLE_FMT_ASYNC "OFF")
endif()
option(LOG4CXX_CONFIG_VAR_EXPANSION "Expand \${varname} instances in
LOG4CXX_CONFIGURATION" ON)
@@ -299,6 +301,8 @@ message(STATUS " character encoding .............. :
${LOG4CXX_CHARSET}")
message(STATUS " Networking support .............. :
${LOG4CXX_NETWORKING_SUPPORT}")
message(STATUS " DOMConfigurator support ......... :
${LOG4CXX_DOMCONFIGURATOR_SUPPORT}")
message(STATUS " Qt support ...................... : ${LOG4CXX_QT_SUPPORT}")
+message(STATUS " LOG4CXX_XXXX_ASYNC macros ....... : ON")
+message(STATUS " LOG4CXX_XXXX_FMT_ASYNC macros ... : ${ENABLE_FMT_ASYNC}")
message(STATUS "C++ version and Boost settings:")
message(STATUS " Prefer boost: ................... : ${PREFER_BOOST}")
message(STATUS " make_unique implementation :..... : ${STD_MAKE_UNIQUE_IMPL}")
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..fc46f44a
--- /dev/null
+++ b/src/examples/cpp/format-async.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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>
+
+using namespace log4cxx;
+using OutputStreamType = std::basic_ostream<logchar>;
+
+struct MyStruct {
+ int x;
+};
+ OutputStreamType&
+operator<<(OutputStreamType& stream, const MyStruct& mystruct )
+{
+ stream << LOG4CXX_STR("[MyStruct x: ") << mystruct.x <<
LOG4CXX_STR("]");
+ return stream;
+}
+#if FMT_VERSION >= (9 * 10000)
+template <> struct fmt::formatter<MyStruct, logchar> :
fmt::basic_ostream_formatter<logchar> {};
+#endif
+
+int main()
+{
+ setlocale(LC_ALL, "");
+
+ BasicConfigurator::configureAsync();
+ auto rootLogger = LogManager::getRootLogger();
+
+ LOG4CXX_INFO_FMT_ASYNC( rootLogger, "This is a {} message", "char" );
+ LOG4CXX_INFO_FMT_ASYNC( rootLogger, LOG4CXX_STR("This is a {}
message"), LOG4CXX_STR("LogString") );
+ LOG4CXX_INFO_FMT_ASYNC( rootLogger, LOG4CXX_STR("We can also align text
to the {:<10} or {:>10}"), LOG4CXX_STR("left"), LOG4CXX_STR("right") );
+
+ MyStruct mine{ 42 };
+ LOG4CXX_INFO_FMT_ASYNC( rootLogger, LOG4CXX_STR("This custom type {}
can also be logged, since it implements operator<<"), mine );
+
+ LOG4CXX_INFO_ASYNC( rootLogger, LOG4CXX_STR("Numbers can be formatted
with excessive operator<<: ")
+ << std::setprecision(3) << 22.456
+ << LOG4CXX_STR(" And as hex: ")
+ << std::setbase( 16 ) << 123 );
+ LOG4CXX_INFO_FMT_ASYNC( rootLogger, LOG4CXX_STR("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, LOG4CXX_STR("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..4a146bad 100644
--- a/src/main/cpp/asyncbuffer.cpp
+++ b/src/main/cpp/asyncbuffer.cpp
@@ -16,6 +16,7 @@
*/
#include <log4cxx/helpers/asyncbuffer.h>
+#include <log4cxx/helpers/transcoder.h>
namespace LOG4CXX_NS
{
@@ -26,8 +27,59 @@ namespace helpers
struct AsyncBuffer::Private
{
std::vector<MessageBufferAppender> data;
+
+ Private(const MessageBufferAppender& f)
+ : data{ f }
+ {}
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ StringViewType fmt_string;
+ FmtArgStore fmt_args;
+
+ Private(StringViewType&& format_string, FmtArgStore&& args)
+ : fmt_string{ std::move(format_string) }
+ , fmt_args{ std::move(args) }
+ {}
+
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+ WideStringViewType fmt_wstring;
+ WideFmtArgStore fmt_wargs;
+
+ Private(WideStringViewType&& format_string, WideFmtArgStore&& args)
+ : fmt_wstring{ std::move(format_string) }
+ , fmt_wargs{ std::move(args) }
+ {}
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
};
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+void AsyncBuffer::initializeForFmt(StringViewType&& format_string,
FmtArgStore&& 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);
+ }
+}
+
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+void AsyncBuffer::initializeForFmt(WideStringViewType&& format_string,
WideFmtArgStore&& args)
+{
+ if (!m_priv)
+ m_priv = std::make_unique<Private>(std::move(format_string),
std::move(args));
+ else
+ {
+ m_priv->fmt_wstring = std::move(format_string);
+ m_priv->fmt_wargs = std::move(args);
+ }
+}
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
/** An empty buffer.
*/
AsyncBuffer::AsyncBuffer()
@@ -49,16 +101,57 @@ 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();
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ if (result)
+ result = (0 == m_priv->fmt_string.size());
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+ if (result)
+ result = (0 == m_priv->fmt_wstring.size());
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif
+ }
+ 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 LOG4CXX_LOGCHAR_IS_UTF8
+ if (0 < m_priv->fmt_string.size())
+ msg << fmt::vformat(m_priv->fmt_string,
m_priv->fmt_args);
+#if LOG4CXX_WCHAR_T_API
+ if (0 < m_priv->fmt_wstring.size())
+ {
+ LOG4CXX_DECODE_WCHAR(lsMsg,
fmt::vformat(m_priv->fmt_wstring, m_priv->fmt_wargs));
+ msg << lsMsg;
+ }
+#endif // LOG4CXX_WCHAR_T_API
+#endif // LOG4CXX_LOGCHAR_IS_UTF8
+
+#if LOG4CXX_LOGCHAR_IS_WCHAR
+ if (0 < m_priv->fmt_wstring.size())
+ msg << fmt::vformat(m_priv->fmt_wstring,
m_priv->fmt_wargs);
+ if (0 < m_priv->fmt_string.size())
+ {
+ LOG4CXX_DECODE_CHAR(lsMsg,
fmt::vformat(m_priv->fmt_string, m_priv->fmt_args));
+ msg << lsMsg;
+ }
+#endif // LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ }
}
/**
@@ -67,7 +160,15 @@ void AsyncBuffer::renderMessage(LogCharMessageBuffer& msg)
void AsyncBuffer::clear()
{
if (m_priv)
+ {
m_priv->data.clear();
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ m_priv->fmt_string = {};
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+ m_priv->fmt_wstring = {};
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ }
}
/**
@@ -76,8 +177,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..44110ebf 100644
--- a/src/main/include/log4cxx/helpers/asyncbuffer.h
+++ b/src/main/include/log4cxx/helpers/asyncbuffer.h
@@ -21,6 +21,12 @@
#include <log4cxx/helpers/messagebuffer.h>
#include <functional>
#include <vector>
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+#include <fmt/args.h>
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#include <fmt/xchar.h>
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
namespace LOG4CXX_NS
{
@@ -35,7 +41,7 @@ namespace helpers
*/
class LOG4CXX_EXPORT AsyncBuffer
{
-public:
+public: // ...structors
/** An empty buffer.
*/
AsyncBuffer();
@@ -48,6 +54,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 +84,8 @@ public:
return *this;
}
#endif
+
+public: // Accessors
/**
* Has no item been added to this?
*/
@@ -85,13 +94,38 @@ 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
+ using StringViewType = fmt::basic_string_view<char>;
+ using FmtArgStore =
fmt::dynamic_format_arg_store<fmt::format_context>;
+ template <typename... Args>
+ void setMessage(fmt::format_string<Args...> fmt_str, Args&&... args)
+ {
+ auto store = FmtArgStore();
+ ( store.push_back(std::forward<Args>(args)), ...);
+ initializeForFmt(std::move(fmt_str), std::move(store));
+ }
+
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+ using WideStringViewType = fmt::basic_string_view<wchar_t>;
+ using WideFmtArgStore =
fmt::dynamic_format_arg_store<fmt::wformat_context>;
+ template <typename... Args>
+ void setMessage(fmt::wformat_string<Args...> fmt_str, Args&&... args)
+ {
+ auto store = WideFmtArgStore();
+ ( store.push_back(std::forward<Args>(args)), ...);
+ initializeForFmt(std::move(fmt_str), std::move(store));
+ }
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+
private:
AsyncBuffer(const AsyncBuffer&) = delete;
AsyncBuffer& operator=(const AsyncBuffer&) = delete;
@@ -103,6 +137,14 @@ private:
* Append \c function to this buffer.
*/
void append(const MessageBufferAppender& f);
+
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ void initializeForFmt(StringViewType&& format_string, FmtArgStore&&
args);
+
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+ void initializeForFmt(WideStringViewType&& format_string,
WideFmtArgStore&& args);
+#endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
};
} // namespace helpers
@@ -136,8 +178,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 +226,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 +277,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 +325,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 +374,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 +410,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 +453,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/site/markdown/change-report-gh.md
b/src/site/markdown/change-report-gh.md
index 7e4e4cca..48eae411 100644
--- a/src/site/markdown/change-report-gh.md
+++ b/src/site/markdown/change-report-gh.md
@@ -62,7 +62,8 @@ and the LOG4CXX_CONFIGURATION environment variable (see
log4cxx::spi::Configurat
* Console output (Log4cxx internal logging and BasicConfigurator) use a color
per message level by default
\[[#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)\]
+ * \[[#548](https://github.com/apache/logging-log4cxx/pull/548)\]
+ , \[[#554](https://github.com/apache/logging-log4cxx/pull/554)\]
* A simplified way to attach an AsyncAppender to a logger using a
configuration file
\[[#550](https://github.com/apache/logging-log4cxx/pull/550)\]
diff --git a/src/site/markdown/performance.md b/src/site/markdown/performance.md
index 3f033719..e741d018 100644
--- a/src/site/markdown/performance.md
+++ b/src/site/markdown/performance.md
@@ -109,10 +109,10 @@ The "Iterations" column derivation is explained in
[Google Benchmark documentati
| Appending int+float using FMT, pattern: \%m\%n/threads:6 | 537 ns | 3036 ns
| 212844 |
| Appending int+10float using FMT, pattern: \%m\%n | 1671 ns | 1671 ns |
417402 |
| Appending int+10float using FMT, pattern: \%m\%n/threads:6 | 1275 ns | 7297
ns | 96222 |
-| Async, Sending int+10float using FMT | 2190 ns | 2190 ns | 320109 |
-| Async, Sending int+10float using FMT/threads:6 | 1363 ns | 7862 ns | 84306 |
-| Async, Sending int+10float using AsyncBuffer, pattern: \%m\%n | 1226 ns |
1226 ns | 571351 |
-| Async, Sending int+10float using AsyncBuffer, pattern: \%m\%n/threads:6 |
1398 ns | 7902 ns | 89688 |
+| Async, Sending int+10float using FMT and AsyncBuffer | 784 ns | 783 ns |
891953 |
+| Async, Sending int+10float using FMT and AsyncBuffer/threads:6 | 1375 ns |
7692 ns | 88554 |
+| Async, Sending int+10float using operator<< and AsyncBuffer, pattern: \%m\%n
| 1211 ns | 1211 ns | 578034 |
+| Async, Sending int+10float using operator<< and AsyncBuffer, pattern:
\%m\%n/threads:6 | 1351 ns | 7670 ns | 90912 |
| Logging int+float using MessageBuffer, pattern: \%d \%m\%n | 1073 ns | 1073
ns | 656652 |
| Logging int+float using MessageBuffer, pattern: \%d \%m\%n/threads:6 | 1083
ns | 4895 ns | 142776 |
| Logging int+float using MessageBuffer, JSON | 1394 ns | 1394 ns | 507493 |
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 19dcabb1..4eb99f7d 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -18,12 +18,20 @@
option(ENABLE_MULTITHREAD_TEST "Enable multithread test. Note that this test
is very unstable,
it is mostly designed to ensure that we don't crash immediately if exit is
called in a thread" OFF)
+set(TESTING_DEPENDENCY_COMPILE_DEFINITIONS ${LOG4CXX_COMPILE_DEFINITIONS}
${APR_COMPILE_DEFINITIONS} ${APR_UTIL_COMPILE_DEFINITIONS})
+
# Components required by all tests
add_library(testingFramework STATIC abts.cpp appenderskeletontestcase.cpp
logunit.cpp vectorappender.cpp writerappendertestcase.cpp )
-target_compile_definitions(testingFramework PRIVATE
${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS}
${APR_UTIL_COMPILE_DEFINITIONS} )
+target_compile_definitions(testingFramework PRIVATE
${TESTING_DEPENDENCY_COMPILE_DEFINITIONS} )
target_include_directories(testingFramework PRIVATE ${CMAKE_CURRENT_LIST_DIR}
$<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
add_subdirectory(util)
target_sources(testingUtilities PRIVATE xml/xlevel.cpp)
+if(ENABLE_FMT_ASYNC AND MSVC)
+ # This flag tells the MSVC compiler to read all source files
+ # as encoded in UTF-8, which resolves the fmt static_assert.
+ target_compile_options(testingFramework PRIVATE /utf-8)
+ target_compile_options(testingUtilities PRIVATE /utf-8)
+endif()
set( LOG4CXX_TEST_PROGRAM_PATH "" CACHE PATH "Extra path for test programs" )
set( CMAKE_PROGRAM_PATH "${LOG4CXX_TEST_PROGRAM_PATH};${CMAKE_PROGRAM_PATH}" )
@@ -123,7 +131,7 @@ foreach(testName IN LISTS ALL_LOG4CXX_TESTS)
target_link_libraries(${testName} PRIVATE MockCoreFoundation)
endif()
endif()
- target_compile_definitions(${testName} PRIVATE ${TEST_COMPILE_DEFINITIONS}
${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS}
${APR_UTIL_COMPILE_DEFINITIONS} )
+ target_compile_definitions(${testName} PRIVATE ${TEST_COMPILE_DEFINITIONS}
${TESTING_DEPENDENCY_COMPILE_DEFINITIONS} )
target_include_directories(${testName} PRIVATE ${CMAKE_CURRENT_LIST_DIR}
$<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
target_link_libraries(${testName} PRIVATE testingFramework
testingUtilities log4cxx ${APR_LIBRARIES} ${APR_SYSTEM_LIBS} Threads::Threads
${ODBC_LIBRARIES} )
if(HAS_LIBESMTP)
diff --git a/src/test/cpp/asyncappendertestcase.cpp
b/src/test/cpp/asyncappendertestcase.cpp
index 1faa7c81..b9dd8bcc 100644
--- a/src/test/cpp/asyncappendertestcase.cpp
+++ b/src/test/cpp/asyncappendertestcase.cpp
@@ -26,6 +26,7 @@
#include "appenderskeletontestcase.h"
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/varia/fallbackerrorhandler.h>
#include <apr_strings.h>
#include "testchar.h"
@@ -34,8 +35,31 @@
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/file.h>
+#include <ostream>
#include <thread>
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+#include <fmt/core.h>
+#include <fmt/ostream.h>
+#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
+#include <fmt/xchar.h>
+#endif
+struct MyStruct
+{
+ int value;
+};
+using OutputStreamType = std::basic_ostream<log4cxx::logchar>;
+OutputStreamType& operator<<(OutputStreamType& stream, const MyStruct& data)
+{
+ stream << LOG4CXX_STR("[MyStruct value: ") << data.value <<
LOG4CXX_STR("]");
+ return stream;
+}
+
+#if FMT_VERSION >= (9 * 10000)
+template <> struct fmt::formatter<MyStruct, log4cxx::logchar> :
fmt::basic_ostream_formatter<log4cxx::logchar> {};
+#endif
+#endif
+
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::spi;
@@ -143,6 +167,9 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
LOGUNIT_TEST(testAsyncLoggerXML);
#endif
LOGUNIT_TEST(testAsyncLoggerProperties);
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ LOGUNIT_TEST(testAsyncFmtLogging);
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
LOGUNIT_TEST_SUITE_END();
#ifdef _DEBUG
@@ -155,12 +182,12 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
#endif
public:
- void setUp()
+ void setUp() override
{
AppenderSkeletonTestCase::setUp();
}
- void tearDown()
+ void tearDown() override
{
LogManager::shutdown();
AppenderSkeletonTestCase::tearDown();
@@ -626,6 +653,76 @@ class AsyncAppenderTestCase : public
AppenderSkeletonTestCase
LOGUNIT_ASSERT(vectorAppender->isClosed());
}
+#if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
+ /**
+ * Tests asynchronous logging using FMT
+ */
+ void testAsyncFmtLogging()
+ {
+ // Configure Log4cxx
+ AsyncAppenderPtr async;
+ auto r = LogManager::getLoggerRepository();
+ r->ensureIsConfigured([r, &async]()
+ {
+ async = std::make_shared<AsyncAppender>();
+
async->setName(LOG4CXX_STR("withAsyncFmtLogging"));
+ r->getRootLogger()->addAppender(async);
+ r->setConfigured(true);
+ });
+ LOGUNIT_ASSERT(async);
+ auto eventStore = std::make_shared<VectorAppender>();
+ eventStore->setName(LOG4CXX_STR("VectorAppender"));
+ async->addAppender(eventStore);
+
+ // Log some messages
+ auto rootLogger = r->getRootLogger();
+ LOG4CXX_INFO_FMT_ASYNC(rootLogger, LOG4CXX_STR("Hello,
{}"), LOG4CXX_STR("World"));
+ for (MyStruct i = {0}; i.value < 10; ++i.value)
+ {
+ LOG4CXX_INFO_FMT_ASYNC(rootLogger,
LOG4CXX_STR("Hello, {}"), i);
+ }
+ LOG4CXX_INFO_FMT_ASYNC(rootLogger, "Bye bye {}",
"World");
+ async->close();
+
+ // Check all parts of all messages were received
+ auto& events = eventStore->getVector();
+ std::vector<int> messageCount;
+ int eventCount[] = { 0, 0 };
+ for (auto& e : events)
+ {
+ auto message = e->getRenderedMessage();
+ //LogLog::debug(message);
+ auto isNumberedMessage = (message.npos ==
message.find(LOG4CXX_STR("World")));
+ ++eventCount[isNumberedMessage];
+ if (isNumberedMessage)
+ {
+ auto pos = message.rfind(' ');
+ if (message.npos != pos && pos + 1 <
message.size())
+ {
+ try
+ {
+ auto msgNumber =
StringHelper::toInt(message.substr(pos));
+ if (messageCount.size()
<= msgNumber)
+
messageCount.resize(msgNumber + 1);
+
++messageCount[msgNumber];
+ }
+ catch (std::exception const& ex)
+ {
+ LogString msg;
+
helpers::Transcoder::decode(ex.what(), msg);
+ msg += LOG4CXX_STR("
processing\n");
+
helpers::Transcoder::decode(message, msg);
+
helpers::LogLog::warn(msg);
+ }
+ }
+ }
+ }
+ LOGUNIT_ASSERT_EQUAL(int(messageCount.size()),
eventCount[1]);
+ for (auto& count : messageCount)
+ LOGUNIT_ASSERT_EQUAL(count,
messageCount.front());
+ LOGUNIT_ASSERT_EQUAL(2, eventCount[0]);
+ }
+#endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
};
diff --git a/src/test/cpp/benchmark/benchmark.cpp
b/src/test/cpp/benchmark/benchmark.cpp
index 85a348c2..61a6655c 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]
@@ -510,8 +510,8 @@ BENCHMARK_DEFINE_F(benchmarker,
asyncIntPlus10FloatValueFmtBuffer)(benchmark::St
);
}
}
-BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatValueFmtBuffer)->Name("Async, Sending int+10float using
FMT");
-BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatValueFmtBuffer)->Name("Async, Sending int+10float using
FMT")->Threads(benchmarker::threadCount());
+BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatValueFmtBuffer)->Name("Async, Sending int+10float using FMT
and AsyncBuffer");
+BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatValueFmtBuffer)->Name("Async, Sending int+10float using FMT
and AsyncBuffer")->Threads(benchmarker::threadCount());
#endif
BENCHMARK_DEFINE_F(benchmarker,
asyncIntPlus10FloatAsyncBuffer)(benchmark::State& state)
@@ -546,8 +546,8 @@ BENCHMARK_DEFINE_F(benchmarker,
asyncIntPlus10FloatAsyncBuffer)(benchmark::State
);
}
}
-BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatAsyncBuffer)->Name("Async, Sending int+10float using
AsyncBuffer, pattern: %m%n");
-BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatAsyncBuffer)->Name("Async, Sending int+10float using
AsyncBuffer, pattern: %m%n")->Threads(benchmarker::threadCount());
+BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatAsyncBuffer)->Name("Async, Sending int+10float using
operator<< and AsyncBuffer, pattern: %m%n");
+BENCHMARK_REGISTER_F(benchmarker,
asyncIntPlus10FloatAsyncBuffer)->Name("Async, Sending int+10float using
operator<< and AsyncBuffer, pattern:
%m%n")->Threads(benchmarker::threadCount());
BENCHMARK_DEFINE_F(benchmarker,
fileIntPlusFloatValueMessageBuffer)(benchmark::State& state)
{