You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by sw...@apache.org on 2026/02/12 06:01:41 UTC
(logging-log4cxx) 01/01: Prevent message loss when the calculation of a logged value also logs
This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch improve_message_buffer
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit 0378a6eacffff5e7d78142bd7918253f31c13e3e
Author: Stephen Webb <sw...@gmail.com>
AuthorDate: Thu Feb 12 17:01:00 2026 +1100
Prevent message loss when the calculation of a logged value also logs
---
src/main/cpp/messagebuffer.cpp | 15 ++--
src/main/cpp/threadspecificdata.cpp | 86 ++++++++++++++++++++--
.../include/log4cxx/helpers/threadspecificdata.h | 14 +++-
src/test/cpp/helpers/messagebuffertest.cpp | 46 ++++++++++++
4 files changed, 144 insertions(+), 17 deletions(-)
diff --git a/src/main/cpp/messagebuffer.cpp b/src/main/cpp/messagebuffer.cpp
index df908bff..c34a74ba 100644
--- a/src/main/cpp/messagebuffer.cpp
+++ b/src/main/cpp/messagebuffer.cpp
@@ -22,13 +22,7 @@
#endif
#include <log4cxx/helpers/messagebuffer.h>
#include <log4cxx/helpers/transcoder.h>
-#if !defined(LOG4CXX)
- #define LOG4CXX 1
-#endif
-#include <log4cxx/private/log4cxx_private.h>
-#if !LOG4CXX_HAS_THREAD_LOCAL
#include <log4cxx/helpers/threadspecificdata.h>
-#endif
using namespace LOG4CXX_NS::helpers;
@@ -43,6 +37,11 @@ struct StringOrStream
StringOrStream()
: stream(nullptr)
{}
+ ~StringOrStream()
+ {
+ if (this->stream)
+ ThreadSpecificData::releaseStringStream<T>(*this->stream);
+ }
/**
* Move the character buffer from \c buf to \c stream
*/
@@ -51,11 +50,7 @@ struct StringOrStream
if (!this->stream)
{
const static std::basic_ostringstream<T> initialState;
-#if LOG4CXX_HAS_THREAD_LOCAL
- thread_local static std::basic_ostringstream<T> sStream;
-#else
auto& sStream = ThreadSpecificData::getStringStream<T>();
-#endif
this->stream = &sStream;
this->stream->clear();
this->stream->precision(initialState.precision());
diff --git a/src/main/cpp/threadspecificdata.cpp b/src/main/cpp/threadspecificdata.cpp
index ffaf1566..1bd88bf8 100644
--- a/src/main/cpp/threadspecificdata.cpp
+++ b/src/main/cpp/threadspecificdata.cpp
@@ -33,6 +33,7 @@
#include <thread>
#include <mutex>
#include <list>
+#include <array>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;
@@ -49,14 +50,21 @@ struct ThreadSpecificData::ThreadSpecificDataPrivate{
std::shared_ptr<NamePair> pNamePair;
+ template <typename T>
+ struct CountedStringStream
+ {
+ int usage_count{ 0 };
+ std::basic_ostringstream<T> ss;
+ };
+ static const int max_depth = 3;
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
- std::basic_ostringstream<logchar> logchar_stringstream;
+ std::array<CountedStringStream<logchar>, max_depth> logchar_stringstream;
#endif
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
- std::basic_ostringstream<wchar_t> wchar_stringstream;
+ std::array<CountedStringStream<wchar_t>, max_depth> wchar_stringstream;
#endif
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
- std::basic_ostringstream<UniChar> unichar_stringstream;
+ std::array<CountedStringStream<UniChar>, max_depth> unichar_stringstream;
#endif
void setThreadIdName();
@@ -166,21 +174,87 @@ auto ThreadSpecificData::getNames() -> NamePairPtr
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
std::basic_ostringstream<logchar>& ThreadSpecificData::getStream(const logchar&)
{
- return getCurrentData()->m_priv->logchar_stringstream;
+ auto p = getCurrentData()->m_priv.get();
+ auto pItem = &p->logchar_stringstream[0];
+ for (auto& item : p->logchar_stringstream)
+ {
+ pItem = &item;
+ if (0 == item.usage_count)
+ break;
+ }
+ ++pItem->usage_count;
+ return pItem->ss;
+}
+
+void ThreadSpecificData::releaseStream(std::basic_ostringstream<logchar>& ss)
+{
+ auto p = getCurrentData()->m_priv.get();
+ for (auto& item : p->logchar_stringstream)
+ {
+ if (&item.ss == &ss)
+ {
+ --item.usage_count;
+ break;
+ }
+ }
}
#endif
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
std::basic_ostringstream<wchar_t>& ThreadSpecificData::getStream(const wchar_t&)
{
- return getCurrentData()->m_priv->wchar_stringstream;
+ auto p = getCurrentData()->m_priv.get();
+ auto pItem = &p->wchar_stringstream[0];
+ for (auto& item : p->wchar_stringstream)
+ {
+ pItem = &item;
+ if (0 == item.usage_count)
+ break;
+ }
+ ++pItem->usage_count;
+ return pItem->ss;
+}
+
+void ThreadSpecificData::releaseStream(std::basic_ostringstream<wchar_t>& ss)
+{
+ auto p = getCurrentData()->m_priv.get();
+ for (auto& item : p->wchar_stringstream)
+ {
+ if (&item.ss == &ss)
+ {
+ --item.usage_count;
+ break;
+ }
+ }
}
#endif
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
std::basic_ostringstream<UniChar>& ThreadSpecificData::getStream(const UniChar&)
{
- return getCurrentData()->m_priv->unichar_stringstream;
+ auto p = getCurrentData()->m_priv.get();
+ auto pItem = &p->unichar_stringstream[0];
+ for (auto& item : p->unichar_stringstream)
+ {
+ pItem = &item;
+ if (0 == item.usage_count)
+ break;
+ }
+ ++pItem->usage_count;
+ return pItem->ss;
+}
+
+void ThreadSpecificData::releaseStream(std::basic_ostringstream<UniChar>& ss)
+{
+ auto p = getCurrentData()->m_priv.get();
+ for (auto& item : p->unichar_stringstream)
+ {
+ if (&item.ss == &ss)
+ {
+ --item.usage_count;
+ break;
+ }
+ }
}
#endif
diff --git a/src/main/include/log4cxx/helpers/threadspecificdata.h b/src/main/include/log4cxx/helpers/threadspecificdata.h
index f42d29fb..e9a7d6b3 100644
--- a/src/main/include/log4cxx/helpers/threadspecificdata.h
+++ b/src/main/include/log4cxx/helpers/threadspecificdata.h
@@ -73,7 +73,7 @@ class LOG4CXX_EXPORT ThreadSpecificData
MDC::Map& getMap();
/**
- * A character outpur stream only assessable to the current thread
+ * A character output stream only assessable to the current thread
*/
template <typename T>
static std::basic_ostringstream<T>& getStringStream()
@@ -81,6 +81,15 @@ class LOG4CXX_EXPORT ThreadSpecificData
return getStream(T());
}
+ /**
+ * Make \c ss available for use by the current thread
+ */
+ template <typename T>
+ static void releaseStringStream(std::basic_ostringstream<T>& ss)
+ {
+ return releaseStream(ss);
+ }
+
/**
* The names assigned to the current thread
*/
@@ -100,12 +109,15 @@ class LOG4CXX_EXPORT ThreadSpecificData
private:
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
static std::basic_ostringstream<logchar>& getStream(const logchar&);
+ static void releaseStream(std::basic_ostringstream<logchar>&);
#endif
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
static std::basic_ostringstream<wchar_t>& getStream(const wchar_t&);
+ static void releaseStream(std::basic_ostringstream<wchar_t>&);
#endif
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
static std::basic_ostringstream<UniChar>& getStream(const UniChar&);
+ static void releaseStream(std::basic_ostringstream<UniChar>&);
#endif
LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(ThreadSpecificDataPrivate, m_priv)
};
diff --git a/src/test/cpp/helpers/messagebuffertest.cpp b/src/test/cpp/helpers/messagebuffertest.cpp
index e257528d..9b164b35 100644
--- a/src/test/cpp/helpers/messagebuffertest.cpp
+++ b/src/test/cpp/helpers/messagebuffertest.cpp
@@ -24,6 +24,7 @@
#include <log4cxx/logger.h>
#include <log4cxx/propertyconfigurator.h>
#include "util/compare.h"
+#include <cassert>
#if LOG4CXX_CFSTRING_API
#include <CoreFoundation/CFString.h>
@@ -58,6 +59,7 @@ LOGUNIT_CLASS(MessageBufferTest)
#if LOG4CXX_CFSTRING_API
LOGUNIT_TEST(testInsertCFString);
#endif
+ LOGUNIT_TEST(testInsertCalculatedValue);
LOGUNIT_TEST_SUITE_END();
@@ -256,6 +258,50 @@ public:
}
#endif
+ // Use the Gregory-Leibniz series to approximate π
+ static double calculatePi(int terms, int level, int initialTerm = 0)
+ {
+ double pi = 0.0;
+ if (0 < level)
+ {
+ MessageBuffer buf;
+ auto& retval = buf << "level " << level << " pi " << std::setprecision(6) << (pi = calculatePi(terms * 2, level - 1, terms));
+ assert(buf.hasStream());
+ helpers::LogLog::debug(buf.str(retval));
+ pi /= 4; // Divide by 4 to get the value of the previous level
+ }
+ for (int i = initialTerm; i < terms; i++)
+ {
+ if (i % 2 == 0)
+ {
+ pi += 1.0 / (2 * i + 1); // Add for even index
+ }
+ else
+ {
+ pi -= 1.0 / (2 * i + 1); // Subtract for odd index
+ }
+ if ((i + 1) % 500 == 0)
+ {
+ MessageBuffer buf;
+ auto& retval = buf << "level " << level << " term " << i << " pi " << std::setprecision(6) << (pi * 4);
+ assert(buf.hasStream());
+ helpers::LogLog::debug(buf.str(retval));
+ }
+ }
+ return pi * 4; // Multiply by 4 to get Pi
+ }
+
+ // Checks what happens with 4 concurrently active MessageBuffer objects in the same thread, each using using a std::stringstream.
+ // The Log4cxx debug output shows the partially completed message (at the 3rd level) being reset for use by the 4th level MessageBuffer.
+ void testInsertCalculatedValue()
+ {
+ MessageBuffer buf;
+ std::string expectedValue("pi=3.142 calculated pi=3.141 using 4000 terms");
+ auto& retval = buf << "pi=" << std::setprecision(4) << 3.14159265358979323846 << " calculated pi=" << std::setprecision(4) << calculatePi(1000, 2) << " using 4000 terms";
+ LOGUNIT_ASSERT_EQUAL(true, buf.hasStream());
+ LOGUNIT_ASSERT_EQUAL(expectedValue, buf.str(retval));
+ }
+
};
LOGUNIT_TEST_SUITE_REGISTRATION(MessageBufferTest);