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;

Reply via email to