This is an automated email from the ASF dual-hosted git repository. swebb2066 pushed a commit to branch improve_qt_support in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit 51594c6cd94191bb585e67b67034562c283e6f6d Author: Stephen Webb <[email protected]> AuthorDate: Fri Dec 26 12:39:09 2025 +1100 Simplify using the Qt event loop to watch for configuration file change --- src/main/cpp-qt/configuration.cpp | 140 ++++++++++++---------------- src/main/include/log4cxx-qt/configuration.h | 41 ++++---- src/site/markdown/qt-support.md | 4 +- 3 files changed, 88 insertions(+), 97 deletions(-) diff --git a/src/main/cpp-qt/configuration.cpp b/src/main/cpp-qt/configuration.cpp index 29ab7fe9..52b3c39a 100644 --- a/src/main/cpp-qt/configuration.cpp +++ b/src/main/cpp-qt/configuration.cpp @@ -17,115 +17,98 @@ #include <log4cxx-qt/configuration.h> #include <log4cxx-qt/transcoder.h> #include <log4cxx/helpers/loglog.h> +#include <log4cxx/helpers/stringhelper.h> #include <log4cxx/xml/domconfigurator.h> #include <log4cxx/propertyconfigurator.h> - #include <QFileSystemWatcher> #include <QDir> #include <QFile> #include <memory> #include <QDebug> -namespace LOG4CXX_NS -{ -namespace qt +namespace { -using helpers::LogLog; - -static std::unique_ptr<QFileSystemWatcher> watcher; -static QString configFilename; - -static void loadXMLFile(const QString& path){ - QFileInfo fi(configFilename); - if(!fi.exists()){ - return; - } - LOG4CXX_DECODE_QSTRING(lsPath, path); - xml::DOMConfigurator::configure(lsPath); -} +using namespace LOG4CXX_NS; -static void loadPropertiesFile(const QString& path){ - QFileInfo fi(configFilename); - if(!fi.exists()){ - return; - } - LOG4CXX_DECODE_QSTRING(lsPath, path); - PropertyConfigurator::configure(lsPath); +spi::ConfigurationStatus tryLoadFile(const LogString& lsFilename) +{ + return helpers::StringHelper::endsWith(lsFilename, LOG4CXX_STR(".xml")) + ? xml::DOMConfigurator::configure(lsFilename) + : PropertyConfigurator::configure(lsFilename); } -static void dirChanged(const QString&){ - QFileInfo fi(configFilename); - if(fi.exists()){ - // From the Qt docs: - // Note that QFileSystemWatcher stops monitoring files once they have been renamed - // or removed from disk, and directories once they have been removed from disk. - // - // Some text editing programs will replace the file with a new one, which deletes - // the old file(thus causing Qt to remove the watch), so we need to add in the - // file whenever the directory changes. - // See also: https://stackoverflow.com/questions/18300376/qt-qfilesystemwatcher-signal-filechanged-gets-emited-only-once - watcher->addPath(configFilename); - } +spi::ConfigurationStatus tryLoadFile(const QString& filename) +{ + LOG4CXX_DECODE_QSTRING(lsFilename, filename); + return tryLoadFile(lsFilename); } -Configuration::Configuration(){} - -spi::ConfigurationStatus Configuration::tryLoadFile(const QString& filename){ - auto stat = spi:: ConfigurationStatus::NotConfigured; - auto isXML = false; +} // namespace - LOG4CXX_DECODE_QSTRING(lsFilename, filename); - if(filename.endsWith(".xml")){ - stat = xml::DOMConfigurator::configure(lsFilename); - isXML = true; - }else if(filename.endsWith(".properties")){ - stat = PropertyConfigurator::configure(lsFilename); - } - - if( stat == spi::ConfigurationStatus::Configured ){ - watcher = std::make_unique<QFileSystemWatcher>(); - configFilename = filename; - QFileInfo fi(filename); - watcher->addPath(fi.dir().absolutePath()); - watcher->addPath(filename); +namespace LOG4CXX_NS::qt +{ - QObject::connect(watcher.get(), &QFileSystemWatcher::directoryChanged, - &dirChanged); - if(isXML){ - QObject::connect(watcher.get(), &QFileSystemWatcher::fileChanged, - &loadXMLFile); - }else{ - QObject::connect(watcher.get(), &QFileSystemWatcher::fileChanged, - &loadPropertiesFile); - } - } +void Configuration::reconfigureWhenModified(const QString& filename) +{ + static auto watcher = std::make_unique<QFileSystemWatcher>(); + QFileInfo fi(filename); + // From the Qt docs: + // Note that QFileSystemWatcher stops monitoring files once they have been renamed + // or removed from disk, and directories once they have been removed from disk. + // + // Some text editing programs will replace the file with a new one, which deletes + // the old file(thus causing Qt to remove the watch), so we need to add in the + // file whenever the directory changes. + // See also: https://stackoverflow.com/questions/18300376/qt-qfilesystemwatcher-signal-filechanged-gets-emited-only-once + watcher->addPath(fi.dir().absolutePath()); + watcher->addPath(filename); + QObject::connect(watcher.get(), &QFileSystemWatcher::directoryChanged + , [filename](const QString&){ + if (QFileInfo(filename).exists()) + watcher->addPath(filename); + }); + QObject::connect(watcher.get(), &QFileSystemWatcher::fileChanged + , [](const QString& fullPath){ + tryLoadFile(fullPath); + }); +} - return stat; +void Configuration::reconfigureWhenModified(const LogString& lsFilename) +{ + LOG4CXX_ENCODE_QSTRING(filename, lsFilename); + reconfigureWhenModified(filename); } -std::tuple<spi::ConfigurationStatus,QString> -Configuration::configureFromFileAndWatch(const QVector<QString>& directories, - const QVector<QString>& filenames){ - for( QString dir : directories ){ - for( QString fname : filenames ){ + std::tuple<spi::ConfigurationStatus,QString> +Configuration::configureFromFileAndWatch + ( const QVector<QString>& directories + , const QVector<QString>& filenames + ) +{ + for( QString dir : directories ) + { + for( QString fname : filenames ) + { QString candidate_str = dir + "/" + fname; QFile candidate(candidate_str); - if (LogLog::isDebugEnabled()) + if (helpers::LogLog::isDebugEnabled()) { LOG4CXX_DECODE_QSTRING(msg, "Checking file " + candidate_str); - LogLog::debug(msg); + helpers::LogLog::debug(msg); } if (candidate.exists()) { auto configStatus = tryLoadFile(candidate_str); - if( configStatus == spi::ConfigurationStatus::Configured ){ + if( configStatus == spi::ConfigurationStatus::Configured ) + { + reconfigureWhenModified(candidate_str); return {configStatus, candidate_str}; } - if (LogLog::isDebugEnabled()) + if (helpers::LogLog::isDebugEnabled()) { LOG4CXX_DECODE_QSTRING(failmsg, "Unable to load " + candidate_str + ": trying next"); - LogLog::debug(failmsg); + helpers::LogLog::debug(failmsg); } } } @@ -134,5 +117,4 @@ Configuration::configureFromFileAndWatch(const QVector<QString>& directories, return {spi::ConfigurationStatus::NotConfigured, QString()}; } -} //namespace helpers -} //namespace log4cxx +} // namespace LOG4CXX_NS::qt diff --git a/src/main/include/log4cxx-qt/configuration.h b/src/main/include/log4cxx-qt/configuration.h index ea8c393a..2496d0ea 100644 --- a/src/main/include/log4cxx-qt/configuration.h +++ b/src/main/include/log4cxx-qt/configuration.h @@ -22,28 +22,37 @@ #include <log4cxx/log4cxx.h> #include <log4cxx/defaultconfigurator.h> -namespace LOG4CXX_NS { -namespace qt { - -class LOG4CXX_EXPORT Configuration { -private: - Configuration(); - - static LOG4CXX_NS::spi::ConfigurationStatus tryLoadFile(const QString& filename); +namespace LOG4CXX_NS::qt +{ +/// Configuration support methods that use the Qt event loop +/// to reload the configuration file when it is modified. +class LOG4CXX_EXPORT Configuration +{ public: /** - * Configure Log4cxx and watch the file for changes. See also DefaultConfigurator::configureFromFile. + * Select the file to configure Log4cxx and watch the file for changes. See also DefaultConfigurator::configureFromFile. * - * @param directories - * @param filenames - * @return + * @param directories Each directory is checked for each entry in \c filenames + * @param filenames Each file name is checked in each entry in \c directories + * @return the selected file path if Log4cxx was successfully configured + */ + static std::tuple<spi::ConfigurationStatus, QString> configureFromFileAndWatch + ( const QVector<QString>& directories + , const QVector<QString>& filenames + ); + + /** + * Set up a QFileSystemWatcher that will reconfigure Log4cxx when \c fullPath is modified. + */ + static void reconfigureWhenModified(const QString& fullPath); + + /** + * Set up a QFileSystemWatcher that will reconfigure Log4cxx when \c fullPath is modified. */ - static std::tuple<LOG4CXX_NS::spi::ConfigurationStatus,QString> configureFromFileAndWatch(const QVector<QString>& directories, - const QVector<QString>& filenames); + static void reconfigureWhenModified(const LogString& fullPath); }; -} /* namespace qt */ -} /* namespace log4cxx */ +} // namespace LOG4CXX_NS::qt #endif /* LOG4CXX_QT_CONFIGURATION_H */ diff --git a/src/site/markdown/qt-support.md b/src/site/markdown/qt-support.md index 7e0bb13e..06d7b944 100644 --- a/src/site/markdown/qt-support.md +++ b/src/site/markdown/qt-support.md @@ -28,8 +28,8 @@ routed to Log4cxx, a message handler for Qt must be installed. Log4cxx provides a cmake build option `LOG4CXX_QT_SUPPORT=ON` which adds the log4cxx::qt namespace methods -for directing Qt messages to Log4cxx and -using the Qt event loop to process a configuration file change. +for [directing Qt messages to Log4cxx](@ref log4cxx::qt::messageHandler) and +[using the Qt event loop](@ref log4cxx::qt::Configuration) to process a configuration file change. Use the target `log4cxx-qt` instead of `log4cxx` in your `target_link_libraries` cmake directive. Also, including `log4cxx-qt/logger.h` allows you to use QString values
