This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch logger_removal
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git

commit c07cfe7595cdbfbe617ef9cfcc7a276ca885bd3d
Author: Stephen Webb <swebb2...@gmail.com>
AuthorDate: Tue Nov 28 13:17:38 2023 +1100

    Allow removal of an individual logger from the single global repository
---
 src/main/cpp/hierarchy.cpp                | 37 ++++++++++++-
 src/main/cpp/logmanager.cpp               |  8 +++
 src/main/include/log4cxx/hierarchy.h      | 13 +++++
 src/main/include/log4cxx/loggerinstance.h | 88 +++++++++++++++++++++++++++++++
 src/main/include/log4cxx/logmanager.h     | 13 +++++
 src/test/cpp/loggertestcase.cpp           | 21 ++++++++
 6 files changed, 179 insertions(+), 1 deletion(-)

diff --git a/src/main/cpp/hierarchy.cpp b/src/main/cpp/hierarchy.cpp
index c304fab1..4d6725f8 100644
--- a/src/main/cpp/hierarchy.cpp
+++ b/src/main/cpp/hierarchy.cpp
@@ -258,7 +258,7 @@ LoggerPtr Hierarchy::getLogger(const LogString& name,
        {
                result = it->second;
        }
-       if (!result)
+       if (!result && factory)
        {
                LoggerPtr logger(factory->makeNewLoggerInstance(m_priv->pool, 
name));
                logger->setHierarchy(this);
@@ -489,3 +489,38 @@ void Hierarchy::addAppender(AppenderPtr appender)
 {
        m_priv->allAppenders.push_back(appender);
 }
+
+bool Hierarchy::removeLogger(const LogString& name, bool ifNotUsed)
+{
+       auto parentRefCount = [this](const LoggerPtr& child) -> int
+       {
+               int result = 0;
+               for (auto& node : m_priv->provisionNodes)
+               {
+                       if (node.second.end() != std::find(node.second.begin(), 
node.second.end(), child))
+                               ++result;
+               }
+               return result;
+       };
+       bool result = false;
+       std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
+       auto it = m_priv->loggers.find(name);
+       if (it == m_priv->loggers.end())
+               ;
+       else if (ifNotUsed && 1 + parentRefCount(it->second) < 
it->second.use_count())
+               ;
+       else
+       {
+               for (auto& node : m_priv->provisionNodes)
+               {
+                       for (size_t i = node.second.size(); 0 < i; )
+                       {
+                               if (node.second[--i] == it->second)
+                                       node.second.erase(node.second.begin() + 
i);
+                       }
+               }
+               m_priv->loggers.erase(it);
+               result = true;
+       }
+       return result;
+}
diff --git a/src/main/cpp/logmanager.cpp b/src/main/cpp/logmanager.cpp
index 16290758..ae03711a 100644
--- a/src/main/cpp/logmanager.cpp
+++ b/src/main/cpp/logmanager.cpp
@@ -210,3 +210,11 @@ void LogManager::resetConfiguration()
 {
        getLoggerRepository()->resetConfiguration();
 }
+
+bool LogManager::removeLogger(const LogString& name, bool ifNotUsed)
+{
+       bool result = false;
+       if (auto r = dynamic_cast<Hierarchy*>(getLoggerRepository().get()))
+               result = r->removeLogger(name, ifNotUsed);
+       return result;
+}
diff --git a/src/main/include/log4cxx/hierarchy.h 
b/src/main/include/log4cxx/hierarchy.h
index e43f63d2..9254abe2 100644
--- a/src/main/include/log4cxx/hierarchy.h
+++ b/src/main/include/log4cxx/hierarchy.h
@@ -228,6 +228,19 @@ class LOG4CXX_EXPORT Hierarchy : public 
spi::LoggerRepository
 
                void addAppender(AppenderPtr appender);
 
+               /**
+               Remove the \c name Logger from the hierarchy.
+
+               Note: The \c name Logger must be retrieved from the hierarchy
+               *after* any subsequent configuration file change
+               for the newly loaded settings to be used.
+
+               @param name The logger to remove.
+               @param ifNotUsed If true and use_count() indicates there are 
other references, do not remove the Logger and return false.
+               @returns true if \c name Logger was removed from the hierarchy.
+               */
+               bool removeLogger(const LogString& name, bool ifNotUsed = true);
+
        private:
 
                /**
diff --git a/src/main/include/log4cxx/loggerinstance.h 
b/src/main/include/log4cxx/loggerinstance.h
new file mode 100644
index 00000000..1d85699d
--- /dev/null
+++ b/src/main/include/log4cxx/loggerinstance.h
@@ -0,0 +1,88 @@
+/*
+ * 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_LOGGER_INSTANCE_HDR_
+#define LOG4CXX_LOGGER_INSTANCE_HDR_
+
+#include <log4cxx/logmanager.h>
+#include <log4cxx/logger.h>
+
+namespace LOG4CXX_NS
+{
+
+/**
+ * Conditionally removes a Logger at the end of the instance variable's 
lifetime.
+ */
+ class LoggerInstancePtr
+{
+       bool m_hadConfiguration; //!< Did the logger repository hold the 
m_logger before creation of this instance?
+       LoggerPtr m_logger;
+public: // ...structors
+       /// A null LoggerPtr
+       LoggerInstancePtr() : m_hadConfiguration(false)
+       {}
+       /// A separately configurable logger named \c instanceName
+       template <class StringType>
+       LoggerInstancePtr(const StringType& instanceName)
+               : m_hadConfiguration(LogManager::exists(instanceName))
+               , m_logger(LogManager::getLogger(instanceName))
+       {
+       }
+       /// Remove the logger from the single global map
+       ~LoggerInstancePtr()
+       {
+               if (m_logger && !m_hadConfiguration)
+               {
+                       auto name = m_logger->getName();
+                       m_logger.reset(); // Decrease reference count
+                       LogManager::removeLogger(name);
+               }
+       }
+       const LoggerPtr& operator->() const noexcept
+       {
+        return m_logger;
+    }
+
+       operator LoggerPtr&() noexcept
+       {
+               return m_logger;
+       }
+
+       operator const LoggerPtr&() const noexcept
+       {
+               return m_logger;
+       }
+
+       LoggerPtr& value() noexcept
+       {
+               return m_logger;
+       }
+
+       const LoggerPtr& value() const noexcept
+       {
+               return m_logger;
+       }
+private: // Prevent copies and assignment
+       LoggerInstancePtr(const LoggerInstancePtr&) = delete;
+       LoggerInstancePtr(LoggerInstancePtr&&) = delete;
+       LoggerInstancePtr& operator=(const LoggerInstancePtr&) = delete;
+       LoggerInstancePtr& operator=(LoggerInstancePtr&&) = delete;
+};
+
+} // namespace LOG4CXX_NS
+
+#endif // LOG4CXX_LOGGER_INSTANCE_HDR_
diff --git a/src/main/include/log4cxx/logmanager.h 
b/src/main/include/log4cxx/logmanager.h
index 5af0949d..c6e2c152 100644
--- a/src/main/include/log4cxx/logmanager.h
+++ b/src/main/include/log4cxx/logmanager.h
@@ -223,6 +223,19 @@ class LOG4CXX_EXPORT LogManager
                to their default.
                */
                static void resetConfiguration();
+
+               /**
+               Remove the \c name Logger from the hierarchy.
+
+               Note: The \c name Logger must be retrieved from the hierarchy
+               *after* any subsequent configuration file change
+               for the newly loaded settings to be used.
+
+               @param name The logger to remove.
+               @param ifNotUsed If true and use_count() indicates there are 
other references, do not remove the Logger and return false.
+               @returns true if \c name Logger was removed from the hierarchy.
+               */
+               static bool removeLogger(const LogString& name, bool ifNotUsed 
= true);
 }; // class LogManager
 }  // namespace log4cxx
 
diff --git a/src/test/cpp/loggertestcase.cpp b/src/test/cpp/loggertestcase.cpp
index e18f6897..18d68573 100644
--- a/src/test/cpp/loggertestcase.cpp
+++ b/src/test/cpp/loggertestcase.cpp
@@ -23,6 +23,7 @@
 #include <log4cxx/logmanager.h>
 #include <log4cxx/level.h>
 #include <log4cxx/hierarchy.h>
+#include <log4cxx/loggerinstance.h>
 #include <log4cxx/spi/rootlogger.h>
 #include <log4cxx/helpers/propertyresourcebundle.h>
 #include "insertwide.h"
@@ -99,6 +100,7 @@ LOGUNIT_CLASS(LoggerTestCase)
        //    LOGUNIT_TEST(testRB3);
        LOGUNIT_TEST(testExists);
        LOGUNIT_TEST(testHierarchy1);
+       LOGUNIT_TEST(testLoggerInstance);
        LOGUNIT_TEST(testTrace);
        LOGUNIT_TEST(testIsTraceEnabled);
        LOGUNIT_TEST(testAddingListeners);
@@ -449,6 +451,25 @@ public:
                LOGUNIT_ASSERT_EQUAL(true, Logger::isTraceEnabledFor(abc));
        }
 
+       void testLoggerInstance()
+       {
+               std::vector<LogString> instanceNames =
+               { LOG4CXX_TEST_STR("xxx.zzz")
+               , LOG4CXX_TEST_STR("xxx.aaaa")
+               , LOG4CXX_TEST_STR("xxx.bbb")
+               , LOG4CXX_TEST_STR("xxx.ccc")
+               , LOG4CXX_TEST_STR("xxx.ddd")
+               };
+               auto initialCount = LogManager::getCurrentLoggers().size();
+               for (auto loggerName : instanceNames)
+               {
+                       LoggerInstancePtr instanceLogger(loggerName);
+                       instanceLogger->info("Hello, World.");
+               }
+               auto finalCount = LogManager::getCurrentLoggers().size();
+               LOGUNIT_ASSERT_EQUAL(initialCount, finalCount);
+       }
+
        void compileTestForLOGCXX202() const
        {
                //

Reply via email to