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]
