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 1418abab Prevent message loss when the calculation of a logged value
also logs (#594)
1418abab is described below
commit 1418abab35726d3ec780e63bbd48d7a3f3cdec10
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Feb 14 09:54:14 2026 +1100
Prevent message loss when the calculation of a logged value also logs (#594)
* Allow any number of concurrently active MessageBuffer objects
---
src/main/cpp/messagebuffer.cpp | 15 ++--
src/main/cpp/threadspecificdata.cpp | 83 +++++++++++++++++++---
.../include/log4cxx/helpers/threadspecificdata.h | 19 +++--
src/test/cpp/helpers/messagebuffertest.cpp | 50 +++++++++++++
src/test/cpp/rolling/multiprocessrollingtest.cpp | 5 +-
5 files changed, 146 insertions(+), 26 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..b4ad482b 100644
--- a/src/main/cpp/threadspecificdata.cpp
+++ b/src/main/cpp/threadspecificdata.cpp
@@ -49,14 +49,56 @@ struct ThreadSpecificData::ThreadSpecificDataPrivate{
std::shared_ptr<NamePair> pNamePair;
-#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
- std::basic_ostringstream<logchar> logchar_stringstream;
-#endif
+ template <typename T>
+ struct CountedStringStream
+ {
+ int usage_count{ 0 };
+ std::basic_ostringstream<T> ss;
+ };
+
+ // Find an unused stream buffer or add a new stream buffer in the
collection \c store
+ template <typename T>
+ std::basic_ostringstream<T>& getStream(std::list<CountedStringStream<T>
>& store)
+ {
+ CountedStringStream<T>* pItem{ nullptr };
+ for (auto& item : store)
+ {
+ if (0 == item.usage_count)
+ {
+ pItem = &item;
+ break;
+ }
+ }
+ if (!pItem)
+ {
+ store.emplace_back();
+ pItem = &store.back();
+ }
+ ++pItem->usage_count;
+ return pItem->ss;
+ }
+
+ // Decrement the usage count associated with the stream buffer \c ss in
the collection \c store
+ template <typename T>
+ void releaseStream(std::list<CountedStringStream<T> >& store,
std::basic_ostringstream<T>& ss)
+ {
+ for (auto& item : store)
+ {
+ if (&item.ss == &ss)
+ {
+ --item.usage_count;
+ break;
+ }
+ }
+ }
+
+ std::list<CountedStringStream<char> > char_stringstream;
+
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
- std::basic_ostringstream<wchar_t> wchar_stringstream;
+ std::list<CountedStringStream<wchar_t> > wchar_stringstream;
#endif
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
- std::basic_ostringstream<UniChar> unichar_stringstream;
+ std::list<CountedStringStream<UniChar> > unichar_stringstream;
#endif
void setThreadIdName();
@@ -163,24 +205,43 @@ auto ThreadSpecificData::getNames() -> NamePairPtr
return p ? p->m_priv->pNamePair : std::make_shared<NamePair>();
}
-#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
-std::basic_ostringstream<logchar>& ThreadSpecificData::getStream(const
logchar&)
+std::basic_ostringstream<char>& ThreadSpecificData::getStream(const char&)
{
- return getCurrentData()->m_priv->logchar_stringstream;
+ auto p = getCurrentData();
+ return p->m_priv->getStream(p->m_priv->char_stringstream);
+}
+
+void ThreadSpecificData::releaseStream(std::basic_ostringstream<char>& ss)
+{
+ auto p = getCurrentData();
+ p->m_priv->releaseStream(p->m_priv->char_stringstream, ss);
}
-#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();
+ return p->m_priv->getStream(p->m_priv->wchar_stringstream);
+}
+
+void ThreadSpecificData::releaseStream(std::basic_ostringstream<wchar_t>& ss)
+{
+ auto p = getCurrentData();
+ p->m_priv->releaseStream(p->m_priv->wchar_stringstream, ss);
}
#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();
+ return p->m_priv->getStream(p->m_priv->unichar_stringstream);
+}
+
+void ThreadSpecificData::releaseStream(std::basic_ostringstream<UniChar>& ss)
+{
+ auto p = getCurrentData();
+ p->m_priv->releaseStream(p->m_priv->unichar_stringstream, ss);
}
#endif
diff --git a/src/main/include/log4cxx/helpers/threadspecificdata.h
b/src/main/include/log4cxx/helpers/threadspecificdata.h
index f42d29fb..cf042f80 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)
+ {
+ releaseStream(ss);
+ }
+
/**
* The names assigned to the current thread
*/
@@ -98,14 +107,16 @@ class LOG4CXX_EXPORT ThreadSpecificData
*/
static NamePairPtr getNames();
private:
-#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
- static std::basic_ostringstream<logchar>& getStream(const
logchar&);
-#endif
+ static std::basic_ostringstream<char>& getStream(const char&);
+ static void releaseStream(std::basic_ostringstream<char>&);
+
#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..e9601de9 100644
--- a/src/test/cpp/helpers/messagebuffertest.cpp
+++ b/src/test/cpp/helpers/messagebuffertest.cpp
@@ -21,9 +21,11 @@
#include "../logunit.h"
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/logger.h>
#include <log4cxx/propertyconfigurator.h>
#include "util/compare.h"
+#include <cassert>
#if LOG4CXX_CFSTRING_API
#include <CoreFoundation/CFString.h>
@@ -58,6 +60,7 @@ LOGUNIT_CLASS(MessageBufferTest)
#if LOG4CXX_CFSTRING_API
LOGUNIT_TEST(testInsertCFString);
#endif
+ LOGUNIT_TEST(testInsertCalculatedValue);
LOGUNIT_TEST_SUITE_END();
@@ -256,6 +259,53 @@ 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 << LOG4CXX_STR("level ") << level <<
" pi " << std::setprecision(6) << (pi = calculatePi(terms * 2, level - 1,
terms));
+ assert(buf.hasStream());
+ auto msg = buf.str(retval);
+ LOG4CXX_DECODE_CHAR(lsMsg, msg);
+ helpers::LogLog::debug(lsMsg);
+ 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 << LOG4CXX_STR("level ") <<
level << " term " << i << " pi " << std::setprecision(6) << (pi * 4);
+ assert(buf.hasStream());
+ auto msg = buf.str(retval);
+ LOG4CXX_DECODE_CHAR(lsMsg, msg);
+ helpers::LogLog::debug(lsMsg);
+ }
+ }
+ 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.
+ 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);
diff --git a/src/test/cpp/rolling/multiprocessrollingtest.cpp
b/src/test/cpp/rolling/multiprocessrollingtest.cpp
index 91356f3d..0531d72e 100644
--- a/src/test/cpp/rolling/multiprocessrollingtest.cpp
+++ b/src/test/cpp/rolling/multiprocessrollingtest.cpp
@@ -193,7 +193,10 @@ public:
auto thisProgram = GetExecutableFileName();
bool thisProgramExists = std::filesystem::exists(thisProgram);
if (!thisProgramExists && helpers::LogLog::isDebugEnabled())
- helpers::LogLog::debug(LOG4CXX_STR("thisProgram: ") +
thisProgram);
+ {
+ LOG4CXX_DECODE_CHAR(lsProgram, thisProgram);
+ helpers::LogLog::debug(LOG4CXX_STR("thisProgram: ") +
lsProgram);
+ }
LOGUNIT_ASSERT(thisProgramExists);
const char* args[] = {thisProgram.c_str(), "test3", 0};
helpers::Pool p;