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 2022/10/03 23:58:39 UTC

[logging-log4cxx] branch next_stable updated: Reduce disabled logger overhead (#135)

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

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


The following commit(s) were added to refs/heads/next_stable by this push:
     new 91425a0a Reduce disabled logger overhead (#135)
91425a0a is described below

commit 91425a0afdfca2205d548dd9184bf1cab84f4e24
Author: Stephen Webb <st...@ieee.org>
AuthorDate: Tue Oct 4 10:58:35 2022 +1100

    Reduce disabled logger overhead (#135)
    
    * Improve robustness by not crashing when the logger pointer is NULL
---
 src/main/cpp/hierarchy.cpp                      | 117 ++++++++++++++----------
 src/main/cpp/logger.cpp                         |  14 ++-
 src/main/cpp/logmanager.cpp                     |  15 ++-
 src/main/include/log4cxx/hierarchy.h            |  67 ++++++++------
 src/main/include/log4cxx/logger.h               |  55 ++++++++---
 src/main/include/log4cxx/spi/loggerrepository.h |  13 ++-
 src/test/cpp/loggertestcase.cpp                 |  11 +++
 7 files changed, 190 insertions(+), 102 deletions(-)

diff --git a/src/main/cpp/hierarchy.cpp b/src/main/cpp/hierarchy.cpp
index 957ba0e6..b8c0c0a9 100644
--- a/src/main/cpp/hierarchy.cpp
+++ b/src/main/cpp/hierarchy.cpp
@@ -15,10 +15,6 @@
  * limitations under the License.
  */
 
-#if defined(_MSC_VER)
-	#pragma warning ( disable: 4231 4251 4275 4786 )
-#endif
-
 #include <log4cxx/logstring.h>
 #include <log4cxx/spi/loggerfactory.h>
 #include <log4cxx/hierarchy.h>
@@ -53,35 +49,27 @@ typedef std::map<LogString, ProvisionNode> ProvisionNodeMap;
 struct Hierarchy::HierarchyPrivate
 {
 	HierarchyPrivate()
+		: configured(false)
+		, emittedNoAppenderWarning(false)
+		, emittedNoResourceBundleWarning(false)
+		, thresholdInt(Level::ALL_INT)
 	{
-		root = std::make_shared<RootLogger>(pool, Level::getDebug());
-		defaultFactory = std::make_shared<DefaultLoggerFactory>();
-		emittedNoAppenderWarning = false;
-		configured = false;
-		thresholdInt = Level::ALL_INT;
-		threshold = Level::getAll();
-		emittedNoResourceBundleWarning = false;
 	}
 
-	log4cxx::helpers::Pool pool;
+	helpers::Pool pool;
 	mutable std::mutex mutex;
-	bool configured;
 	mutable std::mutex configuredMutex;
+	bool configured;
+	bool emittedNoAppenderWarning;
+	bool emittedNoResourceBundleWarning;
+	int thresholdInt;
 
-	spi::LoggerFactoryPtr defaultFactory;
 	spi::HierarchyEventListenerList listeners;
-
-	LoggerMap loggers;
-
-	ProvisionNodeMap provisionNodes;
-
 	LoggerPtr root;
-
-	int thresholdInt;
 	LevelPtr threshold;
+	LoggerMap loggers;
+	ProvisionNodeMap provisionNodes;
 
-	bool emittedNoAppenderWarning;
-	bool emittedNoResourceBundleWarning;
 };
 
 IMPLEMENT_LOG4CXX_OBJECT(Hierarchy)
@@ -102,8 +90,11 @@ Hierarchy::~Hierarchy()
 			pLogger->removeAllAppenders();
 		}
 	}
-	m_priv->root->removeHierarchy();
-	m_priv->root->removeAllAppenders();
+	if (m_priv->root)
+	{
+		m_priv->root->removeHierarchy();
+		m_priv->root->removeAllAppenders();
+	}
 }
 
 void Hierarchy::addHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener)
@@ -233,19 +224,21 @@ void Hierarchy::fireRemoveAppenderEvent(const Logger* logger, const Appender* ap
 	}
 }
 
-const LevelPtr& Hierarchy::getThreshold() const
+LevelPtr Hierarchy::getThreshold() const
 {
-	return m_priv->threshold;
+	return m_priv->threshold ? m_priv->threshold : Level::getAll();
 }
 
 LoggerPtr Hierarchy::getLogger(const LogString& name)
 {
-	return getLogger(name, m_priv->defaultFactory);
+	static spi::LoggerFactoryPtr defaultFactory = std::make_shared<DefaultLoggerFactory>();
+	return getLogger(name, defaultFactory);
 }
 
 LoggerPtr Hierarchy::getLogger(const LogString& name,
 	const spi::LoggerFactoryPtr& factory)
 {
+	auto root = getRootLogger();
 	std::unique_lock<std::mutex> lock(m_priv->mutex);
 
 	LoggerMap::iterator it = m_priv->loggers.find(name);
@@ -269,7 +262,7 @@ LoggerPtr Hierarchy::getLogger(const LogString& name,
 			m_priv->provisionNodes.erase(it2);
 		}
 
-		updateParents(logger);
+		updateParents(logger, root);
 		result = logger;
 	}
 	return result;
@@ -291,32 +284,40 @@ LoggerList Hierarchy::getCurrentLoggers() const
 
 LoggerPtr Hierarchy::getRootLogger() const
 {
+	std::unique_lock<std::mutex> lock(m_priv->mutex);
+	if (!m_priv->root)
+	{
+		m_priv->root = std::make_shared<RootLogger>(m_priv->pool, Level::getDebug());
+		m_priv->root->setHierarchy(const_cast<Hierarchy*>(this));
+	}
+
 	return m_priv->root;
 }
 
 bool Hierarchy::isDisabled(int level) const
 {
-	if (!m_priv->configured) // auto-configuration required?
-	{
-		std::unique_lock<std::mutex> lock(m_priv->configuredMutex);
-		if (!m_priv->configured)
-		{
-			std::shared_ptr<Hierarchy> nonconstThis = std::const_pointer_cast<Hierarchy>(shared_from_this());
-			DefaultConfigurator::configure(nonconstThis);
-			m_priv->configured = true;
-		}
-	}
-
 	return m_priv->thresholdInt > level;
 }
 
+void Hierarchy::autoConfigure()
+{
+	std::unique_lock<std::mutex> lock(m_priv->configuredMutex);
+	if (!m_priv->configured)
+	{
+		DefaultConfigurator::configure(shared_from_this());
+		m_priv->configured = true;
+	}
+}
 
 void Hierarchy::resetConfiguration()
 {
 	std::unique_lock<std::mutex> lock(m_priv->mutex);
 
-	getRootLogger()->setLevel(Level::getDebug());
-	m_priv->root->setResourceBundle(0);
+	if (m_priv->root)
+	{
+		m_priv->root->setLevel(Level::getDebug());
+		m_priv->root->setResourceBundle(0);
+	}
 	setThresholdInternal(Level::getAll());
 
 	shutdownInternal();
@@ -348,7 +349,8 @@ void Hierarchy::shutdownInternal()
 	m_priv->configured = false;
 
 	// begin by closing nested appenders
-	m_priv->root->closeNestedAppenders();
+	if (m_priv->root)
+		m_priv->root->closeNestedAppenders();
 
 	LoggerMap::iterator it, itEnd = m_priv->loggers.end();
 
@@ -359,7 +361,8 @@ void Hierarchy::shutdownInternal()
 	}
 
 	// then, remove all appenders
-	m_priv->root->removeAllAppenders();
+	if (m_priv->root)
+		m_priv->root->removeAllAppenders();
 
 	for (it = m_priv->loggers.begin(); it != itEnd; it++)
 	{
@@ -368,7 +371,7 @@ void Hierarchy::shutdownInternal()
 	}
 }
 
-void Hierarchy::updateParents(LoggerPtr logger)
+void Hierarchy::updateParents(const LoggerPtr& logger, const LoggerPtr& root)
 {
 	const LogString name(logger->getName());
 	size_t length = name.size();
@@ -410,13 +413,12 @@ void Hierarchy::updateParents(LoggerPtr logger)
 	// If we could not find any existing parents, then link with root.
 	if (!parentFound)
 	{
-		logger->setParent( m_priv->root );
+		logger->setParent( root );
 	}
 }
 
-void Hierarchy::updateChildren(ProvisionNode& pn, LoggerPtr logger)
+void Hierarchy::updateChildren(ProvisionNode& pn, const LoggerPtr& logger)
 {
-
 	ProvisionNode::iterator it, itEnd = pn.end();
 
 	for (it = pn.begin(); it != itEnd; it++)
@@ -424,13 +426,29 @@ void Hierarchy::updateChildren(ProvisionNode& pn, LoggerPtr logger)
 		LoggerPtr& l = *it;
 
 		// Unless this child already points to a correct (lower) parent,
-		// make cat.parent point to l.parent and l.parent to cat.
+		// make logger.parent point to l.parent and l.parent to logger.
 		if (!StringHelper::startsWith(l->getParent()->getName(), logger->getName()))
 		{
 			logger->setParent( l->getParent() );
 			l->setParent( logger );
 		}
 	}
+    
+}
+
+void Hierarchy::updateChildren(const Logger* parent)
+{
+	for (auto& item : m_priv->loggers)
+	{
+		for (auto l = item.second; l; l = l->getParent())
+		{
+			if (l->getParent().get() == parent)
+			{
+				item.second->updateThreshold();
+				break;
+			}
+		}
+	}
 }
 
 void Hierarchy::setConfigured(bool newValue)
@@ -449,6 +467,5 @@ bool Hierarchy::isConfigured()
 HierarchyPtr Hierarchy::create()
 {
 	HierarchyPtr ret(new Hierarchy);
-	ret->m_priv->root->setHierarchy(ret.get());
 	return ret;
 }
diff --git a/src/main/cpp/logger.cpp b/src/main/cpp/logger.cpp
index e7460fb1..8c7d8ba5 100644
--- a/src/main/cpp/logger.cpp
+++ b/src/main/cpp/logger.cpp
@@ -23,7 +23,7 @@
 #include <log4cxx/appender.h>
 #include <log4cxx/level.h>
 #include <log4cxx/helpers/loglog.h>
-#include <log4cxx/spi/loggerrepository.h>
+#include <log4cxx/hierarchy.h>
 #include <log4cxx/helpers/stringhelper.h>
 #include <log4cxx/helpers/transcoder.h>
 #include <log4cxx/helpers/appenderattachableimpl.h>
@@ -88,6 +88,7 @@ IMPLEMENT_LOG4CXX_OBJECT(Logger)
 
 Logger::Logger(Pool& p, const LogString& name1)
 	: m_priv(std::make_unique<LoggerPrivate>(p, name1))
+	, m_threshold(0)
 {
 }
 
@@ -480,8 +481,6 @@ void Logger::l7dlog(const LevelPtr& level1, const std::string& key,
 	l7dlog(level1, lkey, location, values);
 }
 
-
-
 void Logger::removeAllAppenders()
 {
 	m_priv->aai.removeAllAppenders();
@@ -515,11 +514,20 @@ void Logger::setHierarchy(spi::LoggerRepository* repository1)
 void Logger::setParent(LoggerPtr parentLogger)
 {
 	m_priv->parent = parentLogger;
+	updateThreshold();
 }
 
 void Logger::setLevel(const LevelPtr level1)
 {
 	m_priv->level = level1;
+	updateThreshold();
+	if (auto rep = dynamic_cast<Hierarchy*>(getHierarchy()))
+		rep->updateChildren(this);
+}
+
+void Logger::updateThreshold()
+{
+	m_threshold = getEffectiveLevel()->toInt();
 }
 
 const LogString& Logger::getName() const
diff --git a/src/main/cpp/logmanager.cpp b/src/main/cpp/logmanager.cpp
index 07fb4afe..e21f8bbe 100644
--- a/src/main/cpp/logmanager.cpp
+++ b/src/main/cpp/logmanager.cpp
@@ -85,7 +85,10 @@ LoggerRepositoryPtr LogManager::getLoggerRepository()
 LoggerPtr LogManager::getRootLogger()
 {
 	// Delegate the actual manufacturing of the logger to the logger repository.
-	return getLoggerRepository()->getRootLogger();
+	auto r = getLoggerRepository();
+	if (!r->isConfigured())
+		r->autoConfigure();
+	return r->getRootLogger();
 }
 
 /**
@@ -93,7 +96,10 @@ Retrieve the appropriate Logger instance.
 */
 LoggerPtr LogManager::getLoggerLS(const LogString& name)
 {
-	return getLoggerRepository()->getLogger(name);
+	auto r = getLoggerRepository();
+	if (!r->isConfigured())
+		r->autoConfigure();
+	return r->getLogger(name);
 }
 
 /**
@@ -103,7 +109,10 @@ LoggerPtr LogManager::getLoggerLS(const LogString& name,
 	const spi::LoggerFactoryPtr& factory)
 {
 	// Delegate the actual manufacturing of the logger to the logger repository.
-	return getLoggerRepository()->getLogger(name, factory);
+	auto r = getLoggerRepository();
+	if (!r->isConfigured())
+		r->autoConfigure();
+	return r->getLogger(name, factory);
 }
 
 LoggerPtr LogManager::getLogger(const std::string& name)
diff --git a/src/main/include/log4cxx/hierarchy.h b/src/main/include/log4cxx/hierarchy.h
index 96aa3ca0..1f3c55a4 100644
--- a/src/main/include/log4cxx/hierarchy.h
+++ b/src/main/include/log4cxx/hierarchy.h
@@ -54,7 +54,6 @@ themselves to the previously created provision node.
 */
 class LOG4CXX_EXPORT Hierarchy :
 	public virtual spi::LoggerRepository,
-	public virtual helpers::Object,
 	public std::enable_shared_from_this<Hierarchy>
 {
 	private:
@@ -77,7 +76,12 @@ class LOG4CXX_EXPORT Hierarchy :
 
 		~Hierarchy();
 
-		void addHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener);
+		void addHierarchyEventListener(const spi::HierarchyEventListenerPtr& listener) override;
+
+		/**
+		 * Load the configuration if not yet configured.
+		 */
+		void autoConfigure() override;
 
 		/**
 		This call will clear all logger definitions from the internal
@@ -89,7 +93,7 @@ class LOG4CXX_EXPORT Hierarchy :
 		*/
 		void clear();
 
-		void emitNoAppenderWarning(const Logger* logger);
+		void emitNoAppenderWarning(const Logger* logger) override;
 
 		/**
 		Check if the named logger exists in the hierarchy. If so return
@@ -103,7 +107,7 @@ class LOG4CXX_EXPORT Hierarchy :
 		/**
 		The string form of {@link #setThreshold(const LevelPtr&) setThreshold}.
 		*/
-		void setThreshold(const LogString& levelStr);
+		void setThreshold(const LogString& levelStr) override;
 
 		/**
 		Enable logging for logging requests with level <code>l</code> or
@@ -111,18 +115,17 @@ class LOG4CXX_EXPORT Hierarchy :
 
 		        @param l The minimum level for which logging requests are sent to
 		their appenders.  */
-		void setThreshold(const LevelPtr& l);
+		void setThreshold(const LevelPtr& l) override;
 
-		void fireAddAppenderEvent(const Logger* logger, const Appender* appender);
+		void fireAddAppenderEvent(const Logger* logger, const Appender* appender) override;
 
-		void fireRemoveAppenderEvent(const Logger* logger,
-			const Appender* appender);
+		void fireRemoveAppenderEvent(const Logger* logger, const Appender* appender) override;
 
 		/**
 		Returns a Level representation of the <code>enable</code>
 		state.
 		*/
-		const LevelPtr& getThreshold() const;
+		LevelPtr getThreshold() const override;
 
 		/**
 		Return a new logger instance named as the first parameter using
@@ -135,7 +138,7 @@ class LOG4CXX_EXPORT Hierarchy :
 		@param name The name of the logger to retrieve.
 
 		*/
-		LoggerPtr getLogger(const LogString& name);
+		LoggerPtr getLogger(const LogString& name) override;
 
 		/**
 		Return a new logger instance named as the first parameter using
@@ -151,7 +154,7 @@ class LOG4CXX_EXPORT Hierarchy :
 
 		*/
 		LoggerPtr getLogger(const LogString& name,
-			const spi::LoggerFactoryPtr& factory);
+			const spi::LoggerFactoryPtr& factory) override;
 
 		/**
 		Returns all the currently defined loggers in this hierarchy as
@@ -164,14 +167,14 @@ class LOG4CXX_EXPORT Hierarchy :
 		/**
 		Get the root of this hierarchy.
 		*/
-		LoggerPtr getRootLogger() const;
+		LoggerPtr getRootLogger() const override;
 
 		/**
 		This method will return <code>true</code> if this repository is
 		disabled for <code>level</code> object passed as parameter and
 		<code>false</code> otherwise. See also the
 		{@link #setThreshold(const LevelPtr&) setThreshold} method.  */
-		bool isDisabled(int level) const;
+		bool isDisabled(int level) const override;
 
 		/**
 		Reset all values contained in this hierarchy instance to their
@@ -186,7 +189,7 @@ class LOG4CXX_EXPORT Hierarchy :
 		<p>This method should be used sparingly and with care as it will
 		block all logging until it is completed.</p>
 		*/
-		void resetConfiguration();
+		void resetConfiguration() override;
 
 		/**
 		Used by subclasses to add a renderer to the hierarchy passed as parameter.
@@ -205,12 +208,16 @@ class LOG4CXX_EXPORT Hierarchy :
 		configurations where a regular appender is attached to a logger
 		and again to a nested appender.
 		*/
-		void shutdown();
+		void shutdown() override;
 
 
-		virtual bool isConfigured();
-		virtual void setConfigured(bool configured);
+		virtual bool isConfigured() override;
+		virtual void setConfigured(bool configured) override;
 
+		/**
+		Refresh the threshold in children of parent
+		*/
+		void updateChildren(const Logger* parent);
 
 	private:
 
@@ -226,30 +233,33 @@ class LOG4CXX_EXPORT Hierarchy :
 
 		/**
 		This method loops through all the *potential* parents of
-		'cat'. There 3 possible cases:
+		\c logger using the logger name.
+		For example, for a logger named "w.x.y.z",
+		loop through "w.x.y", "w.x" and "w", but not "w.x.y.z".
+		There 3 possible cases:
 
-		1) No entry for the potential parent of 'cat' exists
+		1) No entry for the potential parent of "w.x.y.z" exists
 
 		We create a ProvisionNode for this potential parent and insert
-		'cat' in that provision node.
+		"w.x.y.z" in that provision node.
 
 		2) There entry is of type Logger for the potential parent.
 
-		The entry is 'cat's nearest existing parent. We update cat's
+		The entry is "w.x.y.z"'s nearest existing parent. We update "w.x.y.z"'s
 		parent field with this entry. We also break from the loop
 		because updating our parent's parent is our parent's
 		responsibility.
 
 		3) There entry is of type ProvisionNode for this potential parent.
 
-		We add 'cat' to the list of children for this potential parent.
+		We add "w.x.y.z" to the list of children for this potential parent.
 		*/
-		void updateParents(LoggerPtr logger);
+		void updateParents(const LoggerPtr& logger, const LoggerPtr& root);
 
 		/**
 		We update the links for all the children that placed themselves
-		in the provision node 'pn'. The second argument 'cat' is a
-		reference for the newly created Logger, parent of all the
+		in the provision node 'pn'. The \c logger argument is a
+		newly created Logger, a potential parent of all the
 		children in 'pn'
 
 		We loop on all the children 'c' in 'pn':
@@ -257,13 +267,14 @@ class LOG4CXX_EXPORT Hierarchy :
 		If the child 'c' has been already linked to a child of
 		'cat' then there is no need to update 'c'.
 
-		Otherwise, we set cat's parent field to c's parent and set
-		c's parent field to cat.
+		Otherwise, we set the \c logger parent to 'c's parent and set
+		'c's parent field to \c logger.
 		*/
+		void updateChildren(ProvisionNode& pn, const LoggerPtr& logger);
+
 		Hierarchy(const Hierarchy&);
 		Hierarchy& operator=(const Hierarchy&);
 
-		void updateChildren(ProvisionNode& pn, LoggerPtr logger);
 };
 
 }  //namespace log4cxx
diff --git a/src/main/include/log4cxx/logger.h b/src/main/include/log4cxx/logger.h
index f27f78e5..d5b9c9a5 100644
--- a/src/main/include/log4cxx/logger.h
+++ b/src/main/include/log4cxx/logger.h
@@ -59,6 +59,7 @@ class LOG4CXX_EXPORT Logger :
 
 	private:
 		LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(LoggerPrivate, m_priv)
+		int m_threshold; //!< The cached level of this logger
 
 	public:
 		/**
@@ -912,6 +913,10 @@ class LOG4CXX_EXPORT Logger :
 		 *  enabled, <code>false</code> otherwise.
 		 *   */
 		bool isDebugEnabled() const;
+		inline static bool isDebugEnabledFor(const LoggerPtr& logger)
+		{
+			return logger && logger->m_threshold <= Level::DEBUG_INT && logger->isDebugEnabled();
+		}
 
 		/**
 		Check whether this logger is enabled for a given
@@ -932,6 +937,10 @@ class LOG4CXX_EXPORT Logger :
 		for level info, <code>false</code> otherwise.
 		*/
 		bool isInfoEnabled() const;
+		inline static bool isInfoEnabledFor(const LoggerPtr& logger)
+		{
+			return logger && logger->m_threshold <= Level::INFO_INT && logger->isInfoEnabled();
+		}
 
 		/**
 		Check whether this logger is enabled for the warn Level.
@@ -941,6 +950,10 @@ class LOG4CXX_EXPORT Logger :
 		for level warn, <code>false</code> otherwise.
 		*/
 		bool isWarnEnabled() const;
+		inline static bool isWarnEnabledFor(const LoggerPtr& logger)
+		{
+			return logger && logger->m_threshold <= Level::WARN_INT && logger->isWarnEnabled();
+		}
 
 		/**
 		Check whether this logger is enabled for the error Level.
@@ -950,6 +963,10 @@ class LOG4CXX_EXPORT Logger :
 		for level error, <code>false</code> otherwise.
 		*/
 		bool isErrorEnabled() const;
+		inline static bool isErrorEnabledFor(const LoggerPtr& logger)
+		{
+			return logger && logger->m_threshold <= Level::ERROR_INT && logger->isErrorEnabled();
+		}
 
 		/**
 		Check whether this logger is enabled for the fatal Level.
@@ -959,6 +976,10 @@ class LOG4CXX_EXPORT Logger :
 		for level fatal, <code>false</code> otherwise.
 		*/
 		bool isFatalEnabled() const;
+		inline static bool isFatalEnabledFor(const LoggerPtr& logger)
+		{
+			return logger && logger->m_threshold <= Level::FATAL_INT && logger->isFatalEnabled();
+		}
 
 		/**
 		Check whether this logger is enabled for the trace level.
@@ -968,6 +989,10 @@ class LOG4CXX_EXPORT Logger :
 		for level trace, <code>false</code> otherwise.
 		*/
 		bool isTraceEnabled() const;
+		inline static bool isTraceEnabledFor(const LoggerPtr& logger)
+		{
+			return logger && logger->m_threshold <= Level::TRACE_INT && logger->isTraceEnabled();
+		}
 
 		/**
 		Log a localized and parameterized message.
@@ -1407,6 +1432,7 @@ class LOG4CXX_EXPORT Logger :
 		void setHierarchy(spi::LoggerRepository* repository);
 		void setParent(LoggerPtr parentLogger);
 		spi::LoggerRepository* getHierarchy() const;
+		void updateThreshold();
 
 	public:
 		/**
@@ -1667,7 +1693,6 @@ class LOG4CXX_EXPORT Logger :
 		Logger& operator=(const Logger&);
 };
 LOG4CXX_LIST_DEF(LoggerList, LoggerPtr);
-
 }
 
 /** @addtogroup LoggingMacros Logging macros
@@ -1738,7 +1763,7 @@ Logs a message to a specified logger with the DEBUG level.
 @param message the message string to log.
 */
 #define LOG4CXX_DEBUG(logger, message) do { \
-		if (LOG4CXX_UNLIKELY(logger->isDebugEnabled())) {\
+		if (LOG4CXX_UNLIKELY(::log4cxx::Logger::isDebugEnabledFor(logger))) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getDebug(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1749,7 +1774,7 @@ Logs a message to a specified logger with the DEBUG level, formatting with libfm
 @param ... The format string and message to log
 */
 #define LOG4CXX_DEBUG_FMT(logger, ...) do { \
-		if (LOG4CXX_UNLIKELY(logger->isDebugEnabled())) {\
+		if (LOG4CXX_UNLIKELY(::log4cxx::Logger::isDebugEnabledFor(logger))) {\
 			logger->forcedLog(::log4cxx::Level::getDebug(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 #else
 #define LOG4CXX_DEBUG(logger, message)
@@ -1764,7 +1789,7 @@ Logs a message to a specified logger with the TRACE level.
 @param message the message string to log.
 */
 #define LOG4CXX_TRACE(logger, message) do { \
-		if (LOG4CXX_UNLIKELY(logger->isTraceEnabled())) {\
+		if (LOG4CXX_UNLIKELY(::log4cxx::Logger::isTraceEnabledFor(logger))) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getTrace(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1775,7 +1800,7 @@ Logs a message to a specified logger with the TRACE level, formatting with libfm
 @param ... The format string and message to log
 */
 #define LOG4CXX_TRACE_FMT(logger, ...) do { \
-		if (LOG4CXX_UNLIKELY(logger->isTraceEnabled())) {\
+		if (LOG4CXX_UNLIKELY(::log4cxx::Logger::isTraceEnabledFor(logger))) {\
 			logger->forcedLog(::log4cxx::Level::getTrace(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 #else
 #define LOG4CXX_TRACE(logger, message)
@@ -1790,7 +1815,7 @@ Logs a message to a specified logger with the INFO level.
 @param message the message string to log.
 */
 #define LOG4CXX_INFO(logger, message) do { \
-		if (logger->isInfoEnabled()) {\
+		if (::log4cxx::Logger::isInfoEnabledFor(logger)) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getInfo(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1802,7 +1827,7 @@ Logs a message to a specified logger with the INFO level, formatting with libfmt
 @param ... The format string and message to log
 */
 #define LOG4CXX_INFO_FMT(logger, ...) do { \
-		if (logger->isInfoEnabled()) {\
+		if (::log4cxx::Logger::isInfoEnabledFor(logger)) {\
 			logger->forcedLog(::log4cxx::Level::getInfo(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 #else
 #define LOG4CXX_INFO(logger, message)
@@ -1817,7 +1842,7 @@ Logs a message to a specified logger with the WARN level.
 @param message the message string to log.
 */
 #define LOG4CXX_WARN(logger, message) do { \
-		if (logger->isWarnEnabled()) {\
+		if (::log4cxx::Logger::isWarnEnabledFor(logger)) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getWarn(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1828,7 +1853,7 @@ Logs a message to a specified logger with the WARN level, formatting with libfmt
 @param ... The format string and message to log
 */
 #define LOG4CXX_WARN_FMT(logger, ...) do { \
-		if (logger->isWarnEnabled()) {\
+		if (::log4cxx::Logger::isWarnEnabledFor(logger)) {\
 			logger->forcedLog(::log4cxx::Level::getWarn(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 #else
 #define LOG4CXX_WARN(logger, message)
@@ -1843,7 +1868,7 @@ Logs a message to a specified logger with the ERROR level.
 @param message the message string to log.
 */
 #define LOG4CXX_ERROR(logger, message) do { \
-		if (logger->isErrorEnabled()) {\
+		if (::log4cxx::Logger::isErrorEnabledFor(logger)) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getError(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1854,7 +1879,7 @@ Logs a message to a specified logger with the ERROR level, formatting with libfm
 @param ... The format string and message to log
 */
 #define LOG4CXX_ERROR_FMT(logger, ...) do { \
-		if (logger->isErrorEnabled()) {\
+		if (::log4cxx::Logger::isErrorEnabledFor(logger)) {\
 			logger->forcedLog(::log4cxx::Level::getError(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 
 /**
@@ -1865,7 +1890,7 @@ Logs a error if the condition is not true.
 @param message the message string to log.
 */
 #define LOG4CXX_ASSERT(logger, condition, message) do { \
-		if (!(condition) && logger->isErrorEnabled()) {\
+		if (!(condition) && ::log4cxx::Logger::isErrorEnabledFor(logger)) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getError(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1877,7 +1902,7 @@ Logs a error if the condition is not true, formatting with libfmt
 @param ... The format string and message to log
 */
 #define LOG4CXX_ASSERT_FMT(logger, condition, ...) do { \
-		if (!(condition) && logger->isErrorEnabled()) {\
+		if (!(condition) && ::log4cxx::Logger::isErrorEnabledFor(logger)) {\
 			logger->forcedLog(::log4cxx::Level::getError(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 
 #else
@@ -1895,7 +1920,7 @@ Logs a message to a specified logger with the FATAL level.
 @param message the message string to log.
 */
 #define LOG4CXX_FATAL(logger, message) do { \
-		if (logger->isFatalEnabled()) {\
+		if (::log4cxx::Logger::isFatalEnabledFor(logger)) {\
 			::log4cxx::helpers::MessageBuffer oss_; \
 			logger->forcedLog(::log4cxx::Level::getFatal(), oss_.str(oss_ << message), LOG4CXX_LOCATION); }} while (0)
 
@@ -1906,7 +1931,7 @@ Logs a message to a specified logger with the FATAL level, formatting with libfm
 @param ... The format string and message to log
 */
 #define LOG4CXX_FATAL_FMT(logger, ...) do { \
-		if (logger->isFatalEnabled()) {\
+		if (::log4cxx::Logger::isFatalEnabledFor(logger)) {\
 			logger->forcedLog(::log4cxx::Level::getFatal(), fmt::format( __VA_ARGS__ ), LOG4CXX_LOCATION); }} while (0)
 #else
 #define LOG4CXX_FATAL(logger, message)
diff --git a/src/main/include/log4cxx/spi/loggerrepository.h b/src/main/include/log4cxx/spi/loggerrepository.h
index 2dfa3fbc..e76bf16a 100644
--- a/src/main/include/log4cxx/spi/loggerrepository.h
+++ b/src/main/include/log4cxx/spi/loggerrepository.h
@@ -56,6 +56,12 @@ class LOG4CXX_EXPORT LoggerRepository : public virtual helpers::Object
 		*/
 		virtual void addHierarchyEventListener(const HierarchyEventListenerPtr&
 			listener) = 0;
+
+		/**
+		 * Load the configuration if not yet configured.
+		 */
+		virtual void autoConfigure() {};
+
 		/**
 		Is the repository disabled for a given level? The answer depends
 		on the repository threshold and the <code>level</code>
@@ -80,7 +86,7 @@ class LOG4CXX_EXPORT LoggerRepository : public virtual helpers::Object
 		Get the repository-wide threshold. See {@link
 		#setThreshold(const LevelPtr&) setThreshold}
 		            for an explanation. */
-		virtual const LevelPtr& getThreshold() const = 0;
+		virtual LevelPtr getThreshold() const = 0;
 
 		virtual LoggerPtr getLogger(const LogString& name) = 0;
 
@@ -95,8 +101,9 @@ class LOG4CXX_EXPORT LoggerRepository : public virtual helpers::Object
 
 		virtual LoggerList getCurrentLoggers() const = 0;
 
-		virtual void fireAddAppenderEvent(const Logger* logger,
-			const Appender* appender) = 0;
+		virtual void fireAddAppenderEvent(const Logger* logger,	const Appender* appender) {};
+
+		virtual void fireRemoveAppenderEvent(const Logger* logger, const Appender* appender) {};
 
 		virtual void resetConfiguration() = 0;
 
diff --git a/src/test/cpp/loggertestcase.cpp b/src/test/cpp/loggertestcase.cpp
index 1ab5cd78..cafe9912 100644
--- a/src/test/cpp/loggertestcase.cpp
+++ b/src/test/cpp/loggertestcase.cpp
@@ -401,6 +401,17 @@ public:
 
 		LoggerPtr a11 = h->getLogger(LOG4CXX_STR("a"));
 		LOGUNIT_ASSERT_EQUAL(a0, a11);
+
+		LoggerPtr abc = h->getLogger(LOG4CXX_STR("a.b.c"));
+		LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("a.b.c"), abc->getName());
+		LOGUNIT_ASSERT(abc->getLevel() == 0);
+		LOGUNIT_ASSERT(Level::getError() == abc->getEffectiveLevel());
+
+		// Check the threshold is changed in children of root
+		root->setLevel(Level::getTrace());
+		LOGUNIT_ASSERT(Level::getTrace() == abc->getEffectiveLevel());
+		LOGUNIT_ASSERT_EQUAL(true, a0->isTraceEnabled());
+		LOGUNIT_ASSERT_EQUAL(true, abc->isTraceEnabled());
 	}
 
 	void compileTestForLOGCXX202() const