This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
The following commit(s) were added to refs/heads/master by this push:
new b1ced8a3 Simplify using the Qt event loop to watch for configuration
file change (#574)
b1ced8a3 is described below
commit b1ced8a36baa3df96b7ff3858244852063c527de
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Dec 27 11:43:11 2025 +1100
Simplify using the Qt event loop to watch for configuration file change
(#574)
* Add a unit test for qt::Configuration::configureFromFileAndWatch
---
src/main/CMakeLists.txt | 15 ++-
src/main/cpp-qt/CMakeLists.txt | 51 ++++---
src/main/cpp-qt/configuration.cpp | 146 ++++++++++-----------
src/main/include/log4cxx-qt/configuration.h | 45 ++++---
src/site/markdown/qt-support.md | 4 +-
src/test/cpp/CMakeLists.txt | 9 ++
src/test/cpp/qtconfigurationtest.cpp | 132 +++++++++++++++++++
.../resources/input/qtConfigurationTest.properties | 25 ++++
8 files changed, 296 insertions(+), 131 deletions(-)
diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt
index 5134fe18..42fabda7 100644
--- a/src/main/CMakeLists.txt
+++ b/src/main/CMakeLists.txt
@@ -16,9 +16,16 @@
#
add_subdirectory(include)
add_subdirectory(cpp)
-add_subdirectory(cpp-qt)
add_subdirectory(resources)
+# Log4cxx Qt support, if enabled
+if(LOG4CXX_QT_SUPPORT)
+ add_subdirectory(cpp-qt)
+ target_include_directories(log4cxx-qt PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ )
+endif(LOG4CXX_QT_SUPPORT)
+
# setup include directories
include(GNUInstallDirs)
target_include_directories(log4cxx PUBLIC
@@ -34,12 +41,6 @@ if(LOG4CXX_CFSTRING)
endif()
endif()
-if(LOG4CXX_QT_SUPPORT)
- target_include_directories(log4cxx-qt PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
- )
-endif(LOG4CXX_QT_SUPPORT)
-
option(LOG4CXX_ABI_CHECK "Check for ABI changes" OFF)
if(LOG4CXX_ABI_CHECK)
if(NOT "log4cxx" STREQUAL "${LOG4CXX_NS}")
diff --git a/src/main/cpp-qt/CMakeLists.txt b/src/main/cpp-qt/CMakeLists.txt
index 50372d30..a0999533 100644
--- a/src/main/cpp-qt/CMakeLists.txt
+++ b/src/main/cpp-qt/CMakeLists.txt
@@ -15,31 +15,28 @@
# limitations under the License.
#
-# Log4cxx Qt support, if enabled
-if(LOG4CXX_QT_SUPPORT)
- find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
- find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
- add_library(log4cxx-qt)
- if(BUILD_SHARED_LIBS)
- target_compile_definitions(log4cxx-qt PRIVATE LOG4CXX)
- else()
- target_compile_definitions(log4cxx-qt PUBLIC LOG4CXX_STATIC)
- endif()
- add_dependencies(log4cxx-qt log4cxx-qt-include)
- target_compile_definitions(log4cxx-qt PRIVATE "QT_NO_KEYWORDS")
- target_link_libraries(log4cxx-qt Qt${QT_VERSION_MAJOR}::Core log4cxx)
- target_sources(log4cxx-qt
- PRIVATE
- messagehandler.cpp
- configuration.cpp
- )
- set_target_properties(log4cxx-qt PROPERTIES
- VERSION ${LIBLOG4CXX_LIB_VERSION}
- SOVERSION ${LIBLOG4CXX_LIB_SOVERSION}
- OUTPUT_NAME ${LOG4CXX_LIB_NAME}-qt
- PDB_NAME ${LOG4CXX_LIB_NAME}-qt
- COMPILE_PDB_NAME ${LOG4CXX_LIB_NAME}-qt
- EXPORT_NAME ${LOG4CXX_LIB_NAME}-qt
- )
-endif( LOG4CXX_QT_SUPPORT)
+add_library(log4cxx-qt)
+if(BUILD_SHARED_LIBS)
+ target_compile_definitions(log4cxx-qt PRIVATE LOG4CXX)
+else()
+ target_compile_definitions(log4cxx-qt PUBLIC LOG4CXX_STATIC)
+endif()
+add_dependencies(log4cxx-qt log4cxx-qt-include)
+target_compile_definitions(log4cxx-qt PRIVATE "QT_NO_KEYWORDS")
+target_link_libraries(log4cxx-qt Qt${QT_VERSION_MAJOR}::Core log4cxx)
+target_sources(log4cxx-qt
+ PRIVATE
+ messagehandler.cpp
+ configuration.cpp
+)
+set_target_properties(log4cxx-qt PROPERTIES
+ VERSION ${LIBLOG4CXX_LIB_VERSION}
+ SOVERSION ${LIBLOG4CXX_LIB_SOVERSION}
+ OUTPUT_NAME ${LOG4CXX_LIB_NAME}-qt
+ PDB_NAME ${LOG4CXX_LIB_NAME}-qt
+ COMPILE_PDB_NAME ${LOG4CXX_LIB_NAME}-qt
+ EXPORT_NAME ${LOG4CXX_LIB_NAME}-qt
+)
diff --git a/src/main/cpp-qt/configuration.cpp
b/src/main/cpp-qt/configuration.cpp
index 29ab7fe9..1654efd5 100644
--- a/src/main/cpp-qt/configuration.cpp
+++ b/src/main/cpp-qt/configuration.cpp
@@ -17,115 +17,108 @@
#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(){}
+} // namespace
-spi::ConfigurationStatus Configuration::tryLoadFile(const QString& filename){
- auto stat = spi:: ConfigurationStatus::NotConfigured;
- auto isXML = false;
+namespace LOG4CXX_NS::qt
+{
- 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);
+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.absolutePath());
+ if (helpers::LogLog::isDebugEnabled())
+ {
+ LOG4CXX_DECODE_QSTRING(lsDir, fi.absolutePath());
+ helpers::LogLog::debug(LOG4CXX_STR("Watching directory ") +
lsDir);
}
-
- if( stat == spi::ConfigurationStatus::Configured ){
- watcher = std::make_unique<QFileSystemWatcher>();
- configFilename = filename;
- QFileInfo fi(filename);
- watcher->addPath(fi.dir().absolutePath());
- watcher->addPath(filename);
-
- QObject::connect(watcher.get(),
&QFileSystemWatcher::directoryChanged,
- &dirChanged);
- if(isXML){
- QObject::connect(watcher.get(),
&QFileSystemWatcher::fileChanged,
- &loadXMLFile);
- }else{
- QObject::connect(watcher.get(),
&QFileSystemWatcher::fileChanged,
- &loadPropertiesFile);
- }
+ watcher->addPath(fi.absoluteFilePath());
+ if (helpers::LogLog::isDebugEnabled())
+ {
+ LOG4CXX_DECODE_QSTRING(lsFile, fi.absoluteFilePath());
+ helpers::LogLog::debug(LOG4CXX_STR("Watching file ") + lsFile);
}
+ QObject::connect(watcher.get(), &QFileSystemWatcher::directoryChanged
+ , [fi](const QString&){
+ if (fi.exists())
+ watcher->addPath(fi.absoluteFilePath());
+ });
+ 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 +127,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..131dba4d 100644
--- a/src/main/include/log4cxx-qt/configuration.h
+++ b/src/main/include/log4cxx-qt/configuration.h
@@ -17,33 +17,42 @@
#ifndef LOG4CXX_QT_CONFIGURATION_H
#define LOG4CXX_QT_CONFIGURATION_H
+#include <log4cxx/spi/configurator.h>
#include <QString>
#include <QVector>
-#include <log4cxx/log4cxx.h>
-#include <log4cxx/defaultconfigurator.h>
+#include <tuple>
-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
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 4eb99f7d..a3126e53 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -80,6 +80,9 @@ endif()
if(${LOG4CXX_EVENTS_AT_EXIT})
set(ALL_LOG4CXX_TESTS ${ALL_LOG4CXX_TESTS} eventsatexittest)
endif()
+if(LOG4CXX_QT_SUPPORT)
+ list(APPEND ALL_LOG4CXX_TESTS qtconfigurationtest)
+endif(LOG4CXX_QT_SUPPORT)
foreach(fileName IN LISTS ALL_LOG4CXX_TESTS)
add_executable(${fileName} "${fileName}.cpp")
endforeach()
@@ -123,6 +126,11 @@ if(LOG4CXX_CFSTRING)
set(CFSTR_TESTS filetestcase messagebuffertest leveltestcase streamtestcase
transcodertestcase)
endif()
foreach(testName IN LISTS ALL_LOG4CXX_TESTS)
+ if (${testName} STREQUAL "qtconfigurationtest")
+ target_include_directories(${testName} PRIVATE
$<TARGET_PROPERTY:log4cxx-qt,INCLUDE_DIRECTORIES>)
+ target_compile_definitions(${testName} PRIVATE "QT_NO_KEYWORDS")
+ target_link_libraries(${testName} PRIVATE log4cxx-qt)
+ endif()
if (${testName} IN_LIST CFSTR_TESTS)
if(APPLE)
target_compile_options(${testName} PRIVATE "-fconstant-cfstrings")
@@ -137,6 +145,7 @@ foreach(testName IN LISTS ALL_LOG4CXX_TESTS)
if(HAS_LIBESMTP)
target_link_libraries(${testName} PRIVATE ${ESMTP_LIBRARIES})
endif()
+
add_test(NAME ${testName}
COMMAND ${testName} -v
WORKING_DIRECTORY ${UNIT_TEST_WORKING_DIR}
diff --git a/src/test/cpp/qtconfigurationtest.cpp
b/src/test/cpp/qtconfigurationtest.cpp
new file mode 100644
index 00000000..dd025831
--- /dev/null
+++ b/src/test/cpp/qtconfigurationtest.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+#include "logunit.h"
+#include <log4cxx/logger.h>
+#include <log4cxx/logmanager.h>
+#include <log4cxx/helpers/bytebuffer.h>
+#include <log4cxx/helpers/exception.h>
+#include <log4cxx/helpers/fileoutputstream.h>
+#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/optionconverter.h>
+#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/transcoder.h>
+#include <log4cxx-qt/configuration.h>
+#include <log4cxx-qt/transcoder.h>
+#include <apr_file_io.h>
+#include "apr_time.h"
+#include <QCoreApplication>
+#include <QFileInfo>
+
+using namespace log4cxx;
+
+LOGUNIT_CLASS(QtConfigurationTestCase)
+{
+ LOGUNIT_TEST_SUITE(QtConfigurationTestCase);
+ LOGUNIT_TEST(test1);
+ LOGUNIT_TEST_SUITE_END();
+ helpers::Pool m_pool;
+ char m_buf[2048];
+ QString m_configFile;
+ spi::ConfigurationStatus m_status;
+public:
+
+ void copyPropertyFile(const LogString& lsDestDir)
+ {
+ LOG4CXX_ENCODE_CHAR(destDir, lsDestDir);
+ auto status = apr_file_copy
+ ( "input/qtConfigurationTest.properties"
+ , (destDir + "/qtConfigurationTest.properties").c_str()
+ , APR_FPROT_UREAD | APR_FPROT_UWRITE
+ , m_pool.getAPRPool()
+ );
+ if (APR_SUCCESS != status)
+
helpers::LogLog::warn(helpers::Exception::makeMessage(lsDestDir +
LOG4CXX_STR("/qtConfigurationTest.properties"), status));
+ }
+
+ void setUp()
+ {
+ int argc{ 0 };
+ char* argv[] = {nullptr};
+ static QCoreApplication init_once{argc, argv};
+ auto lsTempDir =
helpers::OptionConverter::getSystemProperty(LOG4CXX_STR("TEMP"),
LOG4CXX_STR("/tmp"));
+ copyPropertyFile(lsTempDir);
+ LOG4CXX_ENCODE_QSTRING(qTempDir, lsTempDir);
+ QVector<QString> paths
+ { qTempDir
+ };
+ QVector<QString> names
+ { QString("qtConfigurationTest.properties")
+ };
+ std::tie(m_status, m_configFile) =
qt::Configuration::configureFromFileAndWatch(paths, names);
+ }
+
+ void tearDown()
+ {
+ LogManager::shutdown();
+ apr_file_remove(m_configFile.toUtf8().constData(),
m_pool.getAPRPool());
+ // wait 0.2 sec to ensure the file is really gone on Windows
+ apr_sleep(200000);
+ }
+
+ void test1()
+ {
+ LOGUNIT_ASSERT_EQUAL(m_status,
spi::ConfigurationStatus::Configured);
+ auto debugLogger1 = LogManager::getLogger(LOG4CXX_STR("test1"));
+ LOGUNIT_ASSERT(debugLogger1);
+ LOGUNIT_ASSERT(!debugLogger1->isDebugEnabled());
+ auto debugLogger2 = LogManager::getLogger(LOG4CXX_STR("test2"));
+ LOGUNIT_ASSERT(debugLogger2);
+ LOGUNIT_ASSERT(debugLogger2->isDebugEnabled());
+ LOGUNIT_ASSERT(QFileInfo(m_configFile).exists());
+ // wait 1 sec to ensure the modification time is different to
creation date
+ for (auto i : {1, 2})
+ {
+ QCoreApplication::processEvents();
+ apr_sleep(500000); // 500 ms
+ }
+ auto debugLogger = LogManager::getLogger(LOG4CXX_STR("test3"));
+ LOGUNIT_ASSERT(debugLogger);
+ LOGUNIT_ASSERT(!debugLogger->isDebugEnabled());
+
+ // Append a configuration for test3 logger
+ helpers::ByteBuffer bbuf(m_buf, sizeof(m_buf));
+ int sz = 0;
+ for (const char* p = "\nlog4j.logger.test3=DEBUG\n"; *p; ++p)
+ {
+ bbuf.put(*p);
+ ++sz;
+ }
+ bbuf.position(0);
+ bbuf.limit(sz);
+ LOG4CXX_DECODE_QSTRING(lsConfigFile, m_configFile);
+ helpers::FileOutputStream of(lsConfigFile, true);
+ of.write(bbuf, m_pool);
+ of.flush(m_pool);
+ of.close(m_pool);
+ helpers::LogLog::debug(LOG4CXX_STR("Updated ") + lsConfigFile);
+
+ // wait 0.1 sec for the change to be noticed
+ for (auto i : {1, 2})
+ {
+ QCoreApplication::processEvents();
+ apr_sleep(50000); // 50 ms
+ }
+ LOGUNIT_ASSERT(debugLogger->isDebugEnabled());
+ }
+};
+
+LOGUNIT_TEST_SUITE_REGISTRATION(QtConfigurationTestCase);
diff --git a/src/test/resources/input/qtConfigurationTest.properties
b/src/test/resources/input/qtConfigurationTest.properties
new file mode 100644
index 00000000..11a41434
--- /dev/null
+++ b/src/test/resources/input/qtConfigurationTest.properties
@@ -0,0 +1,25 @@
+# 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.
+#
+log4j.rootCategory=INFO, A1
+
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.log
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%m%n
+
+#log4j.logger.test1=DEBUG
+log4j.logger.test2=DEBUG