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 { //