Stop all file watch dog instances in LogManager::shutdown(). (#121)
This PR aims to address LOG4CXX-451.
Also included is support for automatic start of a watchdog when using automatic configuration by providing a positive integer value in the LOG4CXX_CONFIGURATION_WATCH_SECONDS environment variable.
diff --git a/src/main/cpp/aprinitializer.cpp b/src/main/cpp/aprinitializer.cpp
index 8831412..6e4214c 100644
--- a/src/main/cpp/aprinitializer.cpp
+++ b/src/main/cpp/aprinitializer.cpp
@@ -46,6 +46,7 @@
apr_initialize();
apr_pool_create(&p, NULL);
apr_atomic_init(p);
+ apr_atomic_init(p);
startTime = apr_time_now();
#if APR_HAS_THREADS
apr_status_t stat = apr_threadkey_private_create(&tlsKey, tlsDestruct, p);
@@ -56,25 +57,32 @@
APRInitializer::~APRInitializer()
{
- {
+ stopWatchDogs();
+ isDestructed = true;
#if APR_HAS_THREADS
- std::unique_lock<std::mutex> lock(mutex);
- apr_threadkey_private_delete(tlsKey);
-#endif
-
- for (std::list<FileWatchdog*>::iterator iter = watchdogs.begin();
- iter != watchdogs.end();
- iter++)
- {
- delete *iter;
- }
- }
-
- // TODO LOGCXX-322
-#ifndef APR_HAS_THREADS
+ std::unique_lock<std::mutex> lock(mutex);
+ apr_threadkey_private_delete(tlsKey);
+#else
apr_terminate();
#endif
- isDestructed = true;
+}
+
+void APRInitializer::stopWatchDogs()
+{
+#if APR_HAS_THREADS
+ std::unique_lock<std::mutex> lock(mutex);
+#endif
+
+ while (!watchdogs.empty())
+ {
+ delete watchdogs.back();
+ watchdogs.pop_back();
+ }
+}
+
+void APRInitializer::unregisterAll()
+{
+ getInstance().stopWatchDogs();
}
APRInitializer& APRInitializer::getInstance()
@@ -132,7 +140,7 @@
#if APR_HAS_THREADS
std::unique_lock<std::mutex> lock(this->mutex);
#endif
- this->objects[key] = pObject;
+ this->objects[key] = pObject;
}
const ObjectPtr& APRInitializer::findOrAddObject(size_t key, std::function<ObjectPtr()> creator)
@@ -140,8 +148,8 @@
#if APR_HAS_THREADS
std::unique_lock<std::mutex> lock(this->mutex);
#endif
- auto pItem = this->objects.find(key);
- if (this->objects.end() == pItem)
- pItem = this->objects.emplace(key, creator()).first;
- return pItem->second;
+ auto pItem = this->objects.find(key);
+ if (this->objects.end() == pItem)
+ pItem = this->objects.emplace(key, creator()).first;
+ return pItem->second;
}
diff --git a/src/main/cpp/defaultconfigurator.cpp b/src/main/cpp/defaultconfigurator.cpp
index 4b64298..97a594b 100644
--- a/src/main/cpp/defaultconfigurator.cpp
+++ b/src/main/cpp/defaultconfigurator.cpp
@@ -67,7 +67,8 @@
OptionConverter::selectAndConfigure(
configuration,
configuratorClassName,
- repo);
+ repo,
+ getConfigurationWatchDelay());
}
else
{
@@ -113,5 +114,16 @@
}
+int DefaultConfigurator::getConfigurationWatchDelay()
+{
+ static const LogString LOG4CXX_DEFAULT_CONFIGURATION_WATCH_KEY(LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS"));
+ LogString optionStr = OptionConverter::getSystemProperty(LOG4CXX_DEFAULT_CONFIGURATION_WATCH_KEY, LogString());
+ int milliseconds = 0;
+ if (!optionStr.empty())
+ milliseconds = stoi(optionStr) * 1000;
+ return milliseconds;
+}
+
+
diff --git a/src/main/cpp/filewatchdog.cpp b/src/main/cpp/filewatchdog.cpp
index 264c1eb..7f74de3 100644
--- a/src/main/cpp/filewatchdog.cpp
+++ b/src/main/cpp/filewatchdog.cpp
@@ -24,6 +24,7 @@
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/threadutility.h>
+#include <log4cxx/helpers/stringhelper.h>
#include <functional>
using namespace log4cxx;
@@ -103,6 +104,12 @@
void FileWatchdog::run()
{
+ LogString msg(LOG4CXX_STR("Checking ["));
+ msg += m_priv->file.getPath();
+ msg += LOG4CXX_STR("] at ");
+ StringHelper::toString((int)m_priv->delay, m_priv->pool, msg);
+ msg += LOG4CXX_STR(" ms interval");
+ LogLog::debug(msg);
while (m_priv->interrupted != 0xFFFF)
{
@@ -113,6 +120,10 @@
checkAndConfigure();
}
+ LogString msg2(LOG4CXX_STR("Stop checking ["));
+ msg2 += m_priv->file.getPath();
+ msg2 += LOG4CXX_STR("]");
+ LogLog::debug(msg2);
}
void FileWatchdog::start()
diff --git a/src/main/cpp/logmanager.cpp b/src/main/cpp/logmanager.cpp
index ca21d06..bbd0387 100644
--- a/src/main/cpp/logmanager.cpp
+++ b/src/main/cpp/logmanager.cpp
@@ -200,6 +200,7 @@
void LogManager::shutdown()
{
+ APRInitializer::unregisterAll();
LoggerRepositoryPtr repPtr = getLoggerRepository();
getLoggerRepository()->shutdown();
}
diff --git a/src/main/cpp/optionconverter.cpp b/src/main/cpp/optionconverter.cpp
index 58605d8..9a4b4bc 100644
--- a/src/main/cpp/optionconverter.cpp
+++ b/src/main/cpp/optionconverter.cpp
@@ -36,6 +36,41 @@
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/file.h>
#include <log4cxx/xml/domconfigurator.h>
+#include <log4cxx/logmanager.h>
+#include <apr_general.h>
+#if !defined(LOG4CXX)
+ #define LOG4CXX 1
+#endif
+#include <log4cxx/helpers/aprinitializer.h>
+
+#if APR_HAS_THREADS
+#include <log4cxx/helpers/filewatchdog.h>
+namespace log4cxx
+{
+
+class ConfiguratorWatchdog : public helpers::FileWatchdog
+{
+ spi::ConfiguratorPtr m_config;
+ public:
+ ConfiguratorWatchdog(const spi::ConfiguratorPtr& config, const File& filename)
+ : helpers::FileWatchdog(filename)
+ , m_config(config)
+ {
+ }
+
+ /**
+ Call PropertyConfigurator#doConfigure(const String& configFileName,
+ const spi::LoggerRepositoryPtr& hierarchy) with the
+ <code>filename</code> to reconfigure log4cxx.
+ */
+ void doOnChange()
+ {
+ m_config->doConfigure(file(), LogManager::getLoggerRepository());
+ }
+};
+
+}
+#endif
using namespace log4cxx;
using namespace log4cxx::helpers;
@@ -375,7 +410,7 @@
}
void OptionConverter::selectAndConfigure(const File& configFileName,
- const LogString& _clazz, spi::LoggerRepositoryPtr hierarchy)
+ const LogString& _clazz, spi::LoggerRepositoryPtr hierarchy, int delay)
{
ConfiguratorPtr configurator;
LogString clazz = _clazz;
@@ -410,5 +445,15 @@
configurator = ConfiguratorPtr(new PropertyConfigurator());
}
- configurator->doConfigure(configFileName, hierarchy);
+#if APR_HAS_THREADS
+ if (0 < delay)
+ {
+ auto dog = new ConfiguratorWatchdog(configurator, configFileName);
+ APRInitializer::registerCleanup(dog);
+ dog->setDelay(delay);
+ dog->start();
+ }
+ else
+#endif
+ configurator->doConfigure(configFileName, hierarchy);
}
diff --git a/src/main/include/log4cxx/defaultconfigurator.h b/src/main/include/log4cxx/defaultconfigurator.h
index 77cf6b0..d5d3f55 100644
--- a/src/main/include/log4cxx/defaultconfigurator.h
+++ b/src/main/include/log4cxx/defaultconfigurator.h
@@ -47,6 +47,7 @@
private:
static const LogString getConfigurationFileName();
static const LogString getConfiguratorClass();
+ static int getConfigurationWatchDelay();
diff --git a/src/main/include/log4cxx/helpers/aprinitializer.h b/src/main/include/log4cxx/helpers/aprinitializer.h
index c6edab0..4b2e501 100644
--- a/src/main/include/log4cxx/helpers/aprinitializer.h
+++ b/src/main/include/log4cxx/helpers/aprinitializer.h
@@ -55,6 +55,7 @@
*/
static void registerCleanup(FileWatchdog* watchdog);
static void unregisterCleanup(FileWatchdog* watchdog);
+ static void unregisterAll();
/**
* Store a single instance type ObjectPtr for deletion prior to termination
*/
@@ -78,6 +79,7 @@
private: // Modifiers
void addObject(size_t key, const ObjectPtr& pObject);
const ObjectPtr& findOrAddObject(size_t key, std::function<ObjectPtr()> creator);
+ void stopWatchDogs();
private: // Attributes
apr_pool_t* p;
std::mutex mutex;
diff --git a/src/main/include/log4cxx/helpers/optionconverter.h b/src/main/include/log4cxx/helpers/optionconverter.h
index f3304fd..4b20ac6 100644
--- a/src/main/include/log4cxx/helpers/optionconverter.h
+++ b/src/main/include/log4cxx/helpers/optionconverter.h
@@ -153,9 +153,12 @@
filename pointed to by <code>configFileName</code> ends in '.xml',
in which case DOMConfigurator is used.
@param hierarchy The Hierarchy to act on.
+ @param delay If greater than zero, the milliseconds to sleep
+ between checking if <code>configFileName</code> has been modified
+ and needs to be reloaded.
*/
static void selectAndConfigure(const File& configFileName,
- const LogString& clazz, spi::LoggerRepositoryPtr hierarchy);
+ const LogString& clazz, spi::LoggerRepositoryPtr hierarchy, int delay = 0);
};
} // namespace helpers
} // namespace log4cxx
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index a7758d3..a5e4e91 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -124,7 +124,7 @@
)
elseif(${testName} STREQUAL autoconfiguretestcase)
set_tests_properties(autoconfiguretestcase PROPERTIES
- ENVIRONMENT "LOG4CXX_CONFIGURATION=${UNIT_TEST_WORKING_DIR}/input/autoConfigureTest.properties;PATH=${ESCAPED_PATH}"
+ ENVIRONMENT "LOG4CXX_CONFIGURATION=${UNIT_TEST_WORKING_DIR}/input/autoConfigureTest.properties;LOG4CXX_CONFIGURATION_WATCH_SECONDS=1;PATH=${ESCAPED_PATH}"
)
else()
set_tests_properties(${testName} PROPERTIES
diff --git a/src/test/cpp/autoconfiguretestcase.cpp b/src/test/cpp/autoconfiguretestcase.cpp
index 4953e2e..9954666 100644
--- a/src/test/cpp/autoconfiguretestcase.cpp
+++ b/src/test/cpp/autoconfiguretestcase.cpp
@@ -47,6 +47,7 @@
LOGUNIT_TEST_SUITE(AutoConfigureTestCase);
LOGUNIT_TEST_THREADS(test1, 4);
LOGUNIT_TEST(test2);
+ LOGUNIT_TEST(stop);
LOGUNIT_TEST_SUITE_END();
#ifdef _DEBUG
struct Fixture
@@ -73,6 +74,11 @@
LOGUNIT_ASSERT(debugLogger);
LOGUNIT_ASSERT(debugLogger->isDebugEnabled());
}
+
+ void stop()
+ {
+ LogManager::shutdown();
+ }
};
LOGUNIT_TEST_SUITE_REGISTRATION(AutoConfigureTestCase);