blob: 5639f906a8663e2514bf6e261264003b55d4acdd [file]
/*
* 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.
*/
#ifndef LOG4CXX_ASYNC_BUFFER_H
#define LOG4CXX_ASYNC_BUFFER_H
#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
#if defined(__cpp_concepts) && 202002 <= __cpp_concepts && defined(__GNUC__) && __GNUC__ <= 12
// GCC 12 has broken concepts
#define LOG4CXX_CONCEPTS 0
#elif defined(__cpp_concepts) && 202002 <= __cpp_concepts
#define LOG4CXX_CONCEPTS 1
#else
#define LOG4CXX_CONCEPTS 0
#endif
#if LOG4CXX_CONCEPTS
#include <concepts>
#endif
namespace LOG4CXX_NS
{
namespace helpers
{
/**
* This class is used by the LOG4CXX_INFO_ASYNC and similar
* macros to support insertion operators.
* The class is not intended for use outside of that context.
*/
class LOG4CXX_EXPORT AsyncBuffer
{
public: // ...structors
/** An empty buffer.
*/
AsyncBuffer();
/** A new buffer with the content of \c other
*/
AsyncBuffer(AsyncBuffer&& other);
/** Release resources.
*/
~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.
*/
template <typename T>
AsyncBuffer& operator<<(const T& value)
{
#if LOG4CXX_CONCEPTS
#if LOG4CXX_LOGCHAR_IS_UTF8
if constexpr (requires(std::ostream& buf, T v) { buf << v; })
{
append([value](CharMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
#if LOG4CXX_WCHAR_T_API
else if constexpr (requires(std::wostream& buf, T v) { buf << v; })
{
append([value](WideMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
#endif // LOG4CXX_WCHAR_T_API
else
static_assert(false, "operator<<(std::ostream&) overload must be provided");
#else // !LOG4CXX_LOGCHAR_IS_UTF8
if constexpr (requires(std::wostream& buf, T v) { buf << v; })
{
append([value](WideMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else if constexpr (requires(std::ostream& buf, T v) { buf << v; })
{
append([value](CharMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else
static_assert(false, "operator<<(std::wostream&) overload must be provided");
#endif // !LOG4CXX_LOGCHAR_IS_UTF8
#else // !LOG4CXX_CONCEPTS
append([value](LogCharMessageBuffer& msgBuf)
{
msgBuf << value;
});
#endif // !LOG4CXX_CONCEPTS
return *this;
}
#ifdef __cpp_init_captures // C++ >= 14
/** Append a function to this buffer that will convert \c value to text.
* @param value type must be move-constructable
* @return this buffer.
*/
template <typename T>
AsyncBuffer& operator<<(const T&& rvalue)
{
#if LOG4CXX_CONCEPTS
#if LOG4CXX_LOGCHAR_IS_UTF8
if constexpr (requires(std::ostream& buf, T v) { buf << v; })
{
append([value = std::move(rvalue)](CharMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
#if LOG4CXX_WCHAR_T_API
else if constexpr (requires(std::wostream& buf, T v) { buf << v; })
{
append([value = std::move(rvalue)](WideMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
#endif // LOG4CXX_WCHAR_T_API
else
static_assert(false, "operator<<(std::ostream&) overload must be provided");
#else // !LOG4CXX_LOGCHAR_IS_UTF8
if constexpr (requires(std::wostream& buf, T v) { buf << v; })
{
append([value = std::move(rvalue)](WideMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else if constexpr (requires(std::ostream& buf, T v) { buf << v; })
{
append([value = std::move(rvalue)](CharMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else
static_assert(false, "operator<<(std::wostream&) overload must be provided");
#endif // !LOG4CXX_LOGCHAR_IS_UTF8
#else // !LOG4CXX_CONCEPTS
append([value = std::move(rvalue)](LogCharMessageBuffer& msgBuf)
{
msgBuf << value;
});
#endif // !LOG4CXX_CONCEPTS
return *this;
}
#endif // __cpp_init_captures
public: // Accessors
/**
* Has no item been added to this?
*/
bool empty() const;
/**
* Add text version of buffered values to \c 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;
LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(Private, m_priv)
#if LOG4CXX_CONCEPTS
using MessageBufferAppender = std::function<void(CharMessageBuffer&)>;
/**
* Append \c f to this buffer.
*/
void append(MessageBufferAppender&& f);
#if LOG4CXX_WCHAR_T_API
using WideMessageBufferAppender = std::function<void(WideMessageBuffer&)>;
/**
* Append \c f to this buffer.
*/
void append(WideMessageBufferAppender&& f);
#endif // LOG4CXX_WCHAR_T_API
#else // !LOG4CXX_CONCEPTS
using MessageBufferAppender = std::function<void(LogCharMessageBuffer&)>;
/**
* Append \c f to this buffer.
*/
void append(MessageBufferAppender&& f);
#endif // !LOG4CXX_CONCEPTS
#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
} // namespace LOG4CXX_NS
/** @addtogroup LoggingMacros Logging macros
@{
*/
#if !defined(LOG4CXX_THRESHOLD) || LOG4CXX_THRESHOLD <= 10000
/**
Add a new logging event containing \c message to attached appender(s) if \c logger is enabled for <code>DEBUG</code> events.
\usage
~~~{.cpp}
LOG4CXX_DEBUG_ASYNC(m_log, "AddMesh:"
<< " name " << meshName
<< " type 0x" << std:: hex << traits.Type
<< " materialName " << meshObject.GetMaterialName()
<< " visible? " << traits.IsDefaultVisible
<< " at " << obj->getBoundingBox().getCenter()
<< " +/- " << obj->getBoundingBox().getHalfSize()
);
~~~
@param logger the logger that has the enabled status.
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_DEBUG_ASYNC(logger, message) do { \
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
/**
Add a new logging event containing \c message to attached appender(s) if \c logger is enabled for <code>TRACE</code> events.
\usage
~~~{.cpp}
LOG4CXX_TRACE_ASYNC(m_log, "AddVertex:" << " at " << p << " n " << n << ' ' << color);
~~~
@param logger the logger that has the enabled status.
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_TRACE_ASYNC(logger, message) do { \
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
/**
Add a new logging event containing \c message to attached appender(s) if \c logger is enabled for <code>INFO</code> events.
\usage
~~~{.cpp}
LOG4CXX_INFO_ASYNC(m_log, surface->GetName()
<< " successfully planned " << std::fixed << std::setprecision(1) << ((plannedArea / (plannedArea + unplannedArea)) * 100.0) << "%"
<< " planned area " << std::fixed << std::setprecision(4) << plannedArea << "m^2"
<< " unplanned area " << unplannedArea << "m^2"
<< " planned segments " << surface->GetSegmentPlanCount() << " of " << surface->GetSegmentCount()
);
~~~
@param logger the logger that has the enabled status.
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_INFO_ASYNC(logger, message) do { \
if (::LOG4CXX_NS::Logger::isInfoEnabledFor(logger)) {\
::LOG4CXX_NS::helpers::AsyncBuffer buf;\
logger->addInfoEvent(std::move(buf << message), LOG4CXX_LOCATION);\
}} while (0)
#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
/**
Add a new logging event containing \c message to attached appender(s) if \c logger is enabled for <code>WARN</code> events.
\usage
~~~{.cpp}
catch (const std::exception& ex)
{
LOG4CXX_WARN_ASYNC(m_log, ex.what() << ": in " << m_task->GetParamFilePath());
}
~~~
@param logger the logger to be used.
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_WARN_ASYNC(logger, message) do { \
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
/**
Add a new logging event containing \c message to attached appender(s) if \c logger is enabled for <code>ERROR</code> events.
\usage
~~~{.cpp}
catch (std::exception& ex)
{
LOG4CXX_ERROR_ASYNC(m_log, ex.what() << " in AddScanData");
}
~~~
@param logger the logger to be used.
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_ERROR_ASYNC(logger, message) do { \
if (::LOG4CXX_NS::Logger::isErrorEnabledFor(logger)) {\
::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.
@param logger the logger to be used.
@param condition condition
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_ASSERT_ASYNC(logger, condition, message) do { \
if (!(condition) && ::LOG4CXX_NS::Logger::isErrorEnabledFor(logger)) {\
::LOG4CXX_NS::helpers::AsyncBuffer buf; \
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
/**
Add a new logging event containing \c message to attached appender(s) if \c logger is enabled for <code>FATAL</code> events.
\usage
~~~{.cpp}
LOG4CXX_FATAL_ASYNC(m_log, m_renderSystem->getName() << " is not supported");
~~~
@param logger the logger to be used.
@param message a valid r-value expression of an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOG4CXX_FATAL_ASYNC(logger, message) do { \
if (::LOG4CXX_NS::Logger::isFatalEnabledFor(logger)) {\
::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 */