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 d1257fdc Add support for programmatically providing runtime values for 
configuration option values (#520)
d1257fdc is described below

commit d1257fdcf5ce44d5608203e47d993d84482e7a0f
Author: Stephen Webb <stephen.w...@ieee.org>
AuthorDate: Mon Aug 18 15:55:06 2025 +1000

    Add support for programmatically providing runtime values for configuration 
option values (#520)
    
    * Configuration ${varname} values can be set programatically prior to 
loading a configuration file
    
    * The current executable's file name and its components are available for 
use in a configuration file
    and the LOG4CXX_CONFIGURATION environment variable
    
    * Simplify multiprocessrollingtest
    
    * Update to latest version of vcpkg
    
    * Also start a watchdog when using DefaultConfigurator::configureFromFile
    
    * Support for disabling the expansion of ${varname} instances in 
LOG4CXX_CONFIGURATION environment variable
    
    * Update config3.cpp for MacOS 15
    
    * Prevent MacOS 14 failure by installing CMake from brew to 'force' the 
usage of CMake Version 4 when using g++ on MacOS
    
    * The Configurator implementation is responsible for doing 
'repository->setConfigured(true)'
    
    * Always warn when the configuration file exists and cannot be used
    
    * Use DefaultConfigurator::configureFromFile in a unit test
    
    * Ensure unit test work when std::filesystem::path is unavailable an when 
logchar_t != char
    
    * Improve unit test completeness/robustness
    
    * Fix compilation error
    
    * Ensure FileWatchdog::checkAndConfigure always calls doOnChange() after 
FileWatchdog::setFile()
    
    * Always return the name of the found configuration file from 
configureFromFile
    
    * Show the combinatorial behaviour of 
DefaultConfigurator::configureFromFile more explicitly
---
 .github/workflows/log4cxx-macos.yml                |   3 +
 .github/workflows/log4cxx-windows-static.yml       |   2 +-
 .github/workflows/log4cxx-windows.yml              |   2 +-
 CMakeLists.txt                                     |   7 +
 src/examples/cpp/CMakeLists.txt                    |   2 +-
 src/examples/cpp/auto-configured.xml               |   2 +-
 src/examples/cpp/com/foo/config3.cpp               |   1 +
 src/examples/cpp/com/foo/config4.cpp               |  72 ++++++++++
 .../cpp/com/foo/product_version.h}                 |  20 +--
 src/main/cpp/configurator.cpp                      |  29 ++++
 src/main/cpp/defaultconfigurator.cpp               | 153 +++++++++++----------
 src/main/cpp/domconfigurator.cpp                   |   2 +-
 src/main/cpp/filewatchdog.cpp                      |   1 +
 src/main/cpp/optionconverter.cpp                   |   2 +-
 src/main/cpp/properties.cpp                        |  16 +++
 src/main/cpp/propertyconfigurator.cpp              |   2 +-
 src/main/cpp/system.cpp                            |  87 ++++++++++++
 src/main/include/log4cxx/defaultconfigurator.h     |  93 ++++++++++---
 .../log4cxx/helpers/filesystempath.h}              |  28 ++--
 src/main/include/log4cxx/helpers/properties.h      |  14 +-
 src/main/include/log4cxx/helpers/system.h          |  23 +++-
 .../include/log4cxx/private/log4cxx_private.h.in   |   1 +
 src/main/include/log4cxx/spi/configurator.h        |  19 +++
 src/site/markdown/change-report-gh.md              |  16 +++
 src/site/markdown/example-programs.md              |   5 +-
 src/test/cpp/autoconfiguretestcase.cpp             |  85 +++++++-----
 src/test/cpp/rolling/multiprocessrollingtest.cpp   |  30 +---
 27 files changed, 520 insertions(+), 197 deletions(-)

diff --git a/.github/workflows/log4cxx-macos.yml 
b/.github/workflows/log4cxx-macos.yml
index 55b56d23..e578ce1e 100644
--- a/.github/workflows/log4cxx-macos.yml
+++ b/.github/workflows/log4cxx-macos.yml
@@ -56,6 +56,9 @@ jobs:
         if [ ${{ matrix.os }} != macos-13 ]; then brew install apr-util; fi
         if [ ${{ matrix.odbc }} == ON ]; then brew install unixodbc; fi
         if [ ${{ matrix.qt }} == ON ]; then brew install qt; fi
+        if [ ${{ matrix.cxx }} == "g++-14" -a $(cmake --version | head -1 | 
awk '{split($3, a, "."); print a[1]}') -lt 4 ]
+        then brew install cmake ninja
+        fi
 
     - name: 'configure and build'
       run: |
diff --git a/.github/workflows/log4cxx-windows-static.yml 
b/.github/workflows/log4cxx-windows-static.yml
index 04a08f33..124b57cd 100644
--- a/.github/workflows/log4cxx-windows-static.yml
+++ b/.github/workflows/log4cxx-windows-static.yml
@@ -50,7 +50,7 @@ jobs:
       with:
         repository: microsoft/vcpkg
         path: vcpkg
-        ref: 2024.01.12
+        ref: 2025.07.25
 
     - name: 'Configure Dependencies'
       if: steps.restore-vcpkg-cache.outputs.cache-hit != 'true'
diff --git a/.github/workflows/log4cxx-windows.yml 
b/.github/workflows/log4cxx-windows.yml
index 393d2cd4..e17f30c4 100644
--- a/.github/workflows/log4cxx-windows.yml
+++ b/.github/workflows/log4cxx-windows.yml
@@ -50,7 +50,7 @@ jobs:
       with:
         repository: microsoft/vcpkg
         path: vcpkg
-        ref: 2024.01.12
+        ref: 2025.07.25
 
     - name: 'Configure Dependencies'
       if: steps.restore-vcpkg-cache.outputs.cache-hit != 'true'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5446b2c4..d4bb3e1d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,13 @@ else()
     set(ENABLE_FMT_LAYOUT "OFF")
 endif()
 
+option(LOG4CXX_CONFIG_VAR_EXPANSION "Expand \${varname} instances in 
LOG4CXX_CONFIGURATION" ON)
+if(LOG4CXX_CONFIG_VAR_EXPANSION)
+    set(EXPAND_CONFIG_ENV_VAR 1)
+else()
+    set(EXPAND_CONFIG_ENV_VAR 0)
+endif(LOG4CXX_CONFIG_VAR_EXPANSION)
+
 # Request C++20, if available
 # This *should* fallback to an older standard if it is not available
 if( NOT "${CMAKE_CXX_STANDARD}")
diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index e2487c25..983bb748 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -46,7 +46,7 @@ foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES)
         target_link_libraries(${PROGRAM_NAME} PRIVATE log4cxx-qt)
     endif()
     if(${exampleName} STREQUAL auto-configured)
-        target_sources(${PROGRAM_NAME} PRIVATE com/foo/config3.cpp )
+        target_sources(${PROGRAM_NAME} PRIVATE com/foo/config4.cpp )
     endif()
     target_compile_definitions(${PROGRAM_NAME} PRIVATE 
${EXAMPLE_COMPILE_DEFINITIONS} ${LOG4CXX_COMPILE_DEFINITIONS} 
${APR_COMPILE_DEFINITIONS} ${APR_UTIL_COMPILE_DEFINITIONS} )
     target_include_directories(${PROGRAM_NAME} PRIVATE 
${CMAKE_CURRENT_LIST_DIR} $<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
diff --git a/src/examples/cpp/auto-configured.xml 
b/src/examples/cpp/auto-configured.xml
index 77666bbc..f2e653ca 100644
--- a/src/examples/cpp/auto-configured.xml
+++ b/src/examples/cpp/auto-configured.xml
@@ -3,7 +3,7 @@
   <appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
     <param name="Target" value="System.out"/>
     <layout class="org.apache.log4j.PatternLayout">
-      <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %c %-5p 
- %m%n"/>
+      <param name="ConversionPattern" 
value="${CURRENT_VENDOR_FOLDER}.${CURRENT_PRODUCT_FOLDER}: [%d{yyyy-MM-dd 
HH:mm:ss}]  %c %-5p - %m%n"/>
     </layout>
   </appender>
 
diff --git a/src/examples/cpp/com/foo/config3.cpp 
b/src/examples/cpp/com/foo/config3.cpp
index 33ee90d1..9ded58dd 100644
--- a/src/examples/cpp/com/foo/config3.cpp
+++ b/src/examples/cpp/com/foo/config3.cpp
@@ -52,6 +52,7 @@ auto DefaultConfigurationFileNames(std::string& altPrefix) -> 
std::vector<std::s
        GetModuleFileName(NULL, buf, bufSize);
        pathSepar = '\\';
 #elif defined(__APPLE__)
+       bufCount = bufSize;
        _NSGetExecutablePath(buf, &bufCount);
 #elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
        std::ostringstream exeLink;
diff --git a/src/examples/cpp/com/foo/config4.cpp 
b/src/examples/cpp/com/foo/config4.cpp
new file mode 100644
index 00000000..5aea98ec
--- /dev/null
+++ b/src/examples/cpp/com/foo/config4.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "config.h"
+#include "product_version.h" // Provides getVendorFolder() and 
getProductFolder()
+#include <log4cxx/logmanager.h>
+#include <log4cxx/defaultconfigurator.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/helpers/transcoder.h>
+#include <vector>
+
+namespace com { namespace foo {
+
+// Retrieve the \c name logger pointer.
+// Configure Log4cxx on the first call.
+auto getLogger(const std::string& name) -> LoggerPtr {
+       using namespace log4cxx;
+       static struct log4cxx_initializer {
+               log4cxx_initializer() {
+                       auto vendorFolder = getVendorFolder();
+                       auto productFolder = getProductFolder();
+                       LOG4CXX_DECODE_CHAR(lsVendorFolder, vendorFolder);
+                       LOG4CXX_DECODE_CHAR(lsProductFolder, productFolder);
+
+                       // Allow expansion of ${CURRENT_VENDOR_FOLDER} and 
${CURRENT_PRODUCT_FOLDER}
+                       // when loading a configuration from a file
+                       auto& props = spi::Configurator::properties();
+                       props.setProperty(LOG4CXX_STR("CURRENT_VENDOR_FOLDER"), 
lsVendorFolder);
+                       
props.setProperty(LOG4CXX_STR("CURRENT_PRODUCT_FOLDER"), lsProductFolder);
+
+                       // Check every 5 seconds for configuration file changes
+                       DefaultConfigurator::setConfigurationWatchSeconds(5);
+
+                       // Look for a configuration file in the current working 
directory
+                       // and the same directory as the program
+                       std::vector<LogString> paths
+                               { LOG4CXX_STR(".")
+                               , 
LOG4CXX_STR("${PROGRAM_FILE_PATH.PARENT_PATH}")
+                               };
+                       std::vector<LogString> names
+                               { LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.xml")
+                               , 
LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.properties")
+                               };
+                       auto status       = 
spi::ConfigurationStatus::NotConfigured;
+                       auto selectedPath = LogString();
+                       std::tie(status, selectedPath) = 
DefaultConfigurator::configureFromFile(paths, names);
+                       if (status == spi::ConfigurationStatus::NotConfigured)
+                               BasicConfigurator::configure(); // Send events 
to the console
+               }
+               ~log4cxx_initializer() {
+                       LogManager::shutdown();
+               }
+       } initialiser;
+       return name.empty()
+               ? LogManager::getRootLogger()
+               : LogManager::getLogger(name);
+}
+
+} } // namespace com::foo
diff --git a/src/main/cpp/configurator.cpp 
b/src/examples/cpp/com/foo/product_version.h
similarity index 76%
copy from src/main/cpp/configurator.cpp
copy to src/examples/cpp/com/foo/product_version.h
index 26fa1461..56c51221 100644
--- a/src/main/cpp/configurator.cpp
+++ b/src/examples/cpp/com/foo/product_version.h
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
-#include <log4cxx/spi/configurator.h>
-#include <assert.h>
-#include <log4cxx/logger.h>
-
-using namespace LOG4CXX_NS;
-using namespace LOG4CXX_NS::spi;
-
-IMPLEMENT_LOG4CXX_OBJECT(Configurator)
-
-
-
-
-Configurator::Configurator()
+std::string getVendorFolder()
+{
+  return "ApacheSoftwareFoundation";
+}
+std::string getProductFolder()
 {
+  return "Logging";
 }
diff --git a/src/main/cpp/configurator.cpp b/src/main/cpp/configurator.cpp
index 26fa1461..a10b14f4 100644
--- a/src/main/cpp/configurator.cpp
+++ b/src/main/cpp/configurator.cpp
@@ -19,6 +19,13 @@
 #include <log4cxx/spi/configurator.h>
 #include <assert.h>
 #include <log4cxx/logger.h>
+#include <log4cxx/helpers/singletonholder.h>
+#include <log4cxx/helpers/system.h>
+
+#if !defined(LOG4CXX)
+       #define LOG4CXX 1
+#endif
+#include <log4cxx/helpers/aprinitializer.h>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::spi;
@@ -26,8 +33,30 @@ using namespace LOG4CXX_NS::spi;
 IMPLEMENT_LOG4CXX_OBJECT(Configurator)
 
 
+namespace
+{
+       struct ConfiguratorTag {};
+       using ConfiguratorProperties = std::pair<ConfiguratorTag, 
helpers::Properties>;
 
+       ConfiguratorProperties& getInstance()
+       {
+               using ConfiguratorObject = 
helpers::SingletonHolder<ConfiguratorProperties>;
+               auto result = 
helpers::APRInitializer::getOrAddUnique<ConfiguratorObject>
+                       ( []() -> helpers::ObjectPtr
+                               { return 
std::make_shared<ConfiguratorObject>(); }
+                       );
+               return result->value();
+       }
+}
 
 Configurator::Configurator()
 {
 }
+
+helpers::Properties& Configurator::properties()
+{
+       auto& result = getInstance().second;
+       if (result.isEmpty())
+               helpers::System::addProgramFilePathComponents(result);
+       return result;
+}
diff --git a/src/main/cpp/defaultconfigurator.cpp 
b/src/main/cpp/defaultconfigurator.cpp
index 646d50e3..b0888fd2 100644
--- a/src/main/cpp/defaultconfigurator.cpp
+++ b/src/main/cpp/defaultconfigurator.cpp
@@ -16,46 +16,49 @@
  */
 #include <log4cxx/logstring.h>
 #include <log4cxx/defaultconfigurator.h>
+#include <log4cxx/logmanager.h>
 #include <log4cxx/helpers/pool.h>
 #include <log4cxx/spi/loggerrepository.h>
 #include <log4cxx/file.h>
 #include <log4cxx/helpers/loglog.h>
 #include <log4cxx/helpers/optionconverter.h>
 #include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/system.h>
 #include <log4cxx/xml/domconfigurator.h>
 #include <log4cxx/propertyconfigurator.h>
+#if !defined(LOG4CXX)
+       #define LOG4CXX 1
+#endif
+#include <log4cxx/private/log4cxx_private.h>
 
 using namespace LOG4CXX_NS;
-using namespace LOG4CXX_NS::spi;
-using namespace LOG4CXX_NS::helpers;
-
-namespace
-{
-       LogString DefaultConfiguratorPath;
-       int DefaultConfiguratorWatchSeconds = 0;
-}
+using namespace spi;
+using namespace helpers;
 
 void DefaultConfigurator::setConfigurationFileName(const LogString& path)
 {
-       DefaultConfiguratorPath = path;
+       
Configurator::properties().setProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATION"), 
path);
 }
 
-
 void DefaultConfigurator::setConfigurationWatchSeconds(int seconds)
 {
-       DefaultConfiguratorWatchSeconds = seconds;
+       Pool p;
+       LogString strSeconds;
+       StringHelper::toString(seconds, p, strSeconds);
+       
Configurator::properties().setProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS"),
 strSeconds);
 }
 
-static const int MillisecondsPerSecond = 1000;
+ConfigurationStatus DefaultConfigurator::tryConfigure()
+{
+       auto r = LogManager::getLoggerRepository();
+       configure(r);
+       return r->isConfigured() ? ConfigurationStatus::Configured : 
ConfigurationStatus::NotConfigured;
+}
 
 void DefaultConfigurator::configure(LoggerRepositoryPtr repository)
 {
-       repository->setConfigured(true);
-       const LogString configuratorClassName(getConfiguratorClass());
 
-       LogString configurationFileName = DefaultConfiguratorPath;
-       if (configurationFileName.empty())
-               configurationFileName = getConfigurationFileName();
+       LogString configurationFileName = getConfigurationFileName();
        Pool pool;
        File configuration;
 
@@ -103,12 +106,11 @@ void DefaultConfigurator::configure(LoggerRepositoryPtr 
repository)
                LoggerRepositoryPtr repo(repository);
                OptionConverter::selectAndConfigure(
                        configuration,
-                       configuratorClassName,
+                       getConfiguratorClass(),
                        repo,
-                       0 < DefaultConfiguratorWatchSeconds
-                               ? DefaultConfiguratorWatchSeconds * 
MillisecondsPerSecond
-                               : getConfigurationWatchDelay()
+                       getConfigurationWatchDelay()
                        );
+               // TBD: Report a failure
        }
        else if (LogLog::isDebugEnabled())
        {
@@ -129,83 +131,84 @@ void DefaultConfigurator::configure(LoggerRepositoryPtr 
repository)
 
 const LogString DefaultConfigurator::getConfiguratorClass()
 {
-
-       // Use automatic configration to configure the default hierarchy
-       const LogString log4jConfiguratorClassName(
-               
OptionConverter::getSystemProperty(LOG4CXX_STR("log4j.configuratorClass"), 
LOG4CXX_STR("")));
-       const LogString configuratorClassName(
-               
OptionConverter::getSystemProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATOR_CLASS"),
-                       log4jConfiguratorClassName));
-       return configuratorClassName;
+       auto result = 
System::getProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATOR_CLASS"));
+#if LOG4CXX_VERSION_MAJOR <= 1
+       if (result.empty())
+               result = 
System::getProperty(LOG4CXX_STR("log4j.configuratorClass"));
+#endif
+       return result;
 }
 
 
 const LogString DefaultConfigurator::getConfigurationFileName()
 {
-       static const WideLife<LogString> 
LOG4CXX_DEFAULT_CONFIGURATION_KEY(LOG4CXX_STR("LOG4CXX_CONFIGURATION"));
-       static const WideLife<LogString> 
LOG4J_DEFAULT_CONFIGURATION_KEY(LOG4CXX_STR("log4j.configuration"));
-       const LogString log4jConfigurationFileName(
-               
OptionConverter::getSystemProperty(LOG4J_DEFAULT_CONFIGURATION_KEY, 
LOG4CXX_STR("")));
-       const LogString configurationFileName(
-               
OptionConverter::getSystemProperty(LOG4CXX_DEFAULT_CONFIGURATION_KEY,
-                       log4jConfigurationFileName));
-       return configurationFileName;
+       auto& props = Configurator::properties();
+       LogString configurationFileName = 
props.getProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATION"));
+       bool isEnvVar = false;
+       if (configurationFileName.empty())
+       {
+               configurationFileName = 
System::getProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATION"));
+               isEnvVar = true;
+       }
+#if LOG4CXX_VERSION_MAJOR <= 1
+       if (configurationFileName.empty())
+       {
+               configurationFileName = 
System::getProperty(LOG4CXX_STR("log4j.configuration"));
+               isEnvVar = true;
+       }
+#endif
+#if !LOG4CXX_EXPAND_CONFIG_ENV_VAR
+       if (isEnvVar)
+               return configurationFileName;
+#endif
+       return OptionConverter::substVars(configurationFileName, props);
 }
 
 
 int DefaultConfigurator::getConfigurationWatchDelay()
 {
-       static const WideLife<LogString> 
LOG4CXX_DEFAULT_CONFIGURATION_WATCH_KEY(LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS"));
-       LogString optionStr = 
OptionConverter::getSystemProperty(LOG4CXX_DEFAULT_CONFIGURATION_WATCH_KEY, 
LogString());
+       LogString optionStr = 
Configurator::properties().getProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS"));
+       if (optionStr.empty())
+               optionStr = 
System::getProperty(LOG4CXX_STR("LOG4CXX_CONFIGURATION_WATCH_SECONDS"));
        int milliseconds = 0;
        if (!optionStr.empty())
+       {
+               static const int MillisecondsPerSecond = 1000;
                milliseconds = StringHelper::toInt(optionStr) * 
MillisecondsPerSecond;
-       return milliseconds;
-}
-
-LOG4CXX_NS::spi::ConfigurationStatus DefaultConfigurator::tryLoadFile(const 
LogString& filename){
-#if LOG4CXX_HAS_DOMCONFIGURATOR
-       if(helpers::StringHelper::endsWith(filename, LOG4CXX_STR(".xml"))){
-               return LOG4CXX_NS::xml::DOMConfigurator::configure(filename);
-       }
-#endif
-       if(helpers::StringHelper::endsWith(filename, 
LOG4CXX_STR(".properties"))){
-               return LOG4CXX_NS::PropertyConfigurator::configure(filename);
        }
-
-       return LOG4CXX_NS::spi::ConfigurationStatus::NotConfigured;
+       return milliseconds;
 }
 
-std::tuple<LOG4CXX_NS::spi::ConfigurationStatus,LogString>
-DefaultConfigurator::configureFromFile(const std::vector<LogString>& 
directories, const std::vector<LogString>& filenames){
-       using ResultType = std::tuple<LOG4CXX_NS::spi::ConfigurationStatus, 
LogString>;
-       LOG4CXX_NS::helpers::Pool pool;
+std::tuple<ConfigurationStatus,LogString>
+DefaultConfigurator::configureFromFile(const std::vector<LogString>& 
directories, const std::vector<LogString>& filenames)
+{
+       auto result = std::tuple<ConfigurationStatus, LogString>
+               { ConfigurationStatus::NotConfigured, LogString() };
+       auto r = LogManager::getLoggerRepository();
+       Pool pool;
 
-       for( LogString dir : directories ){
-               for( LogString fname : filenames ){
-                       LogString canidate_str = dir + LOG4CXX_STR("/") + fname;
-                       File candidate(canidate_str);
+       for (auto& dir : directories )
+       {
+               for (auto& fname : filenames )
+               {
+                       setConfigurationFileName(dir + LOG4CXX_STR("/") + 
fname);
+                       auto candidate_str = getConfigurationFileName();
+                       File candidate(candidate_str);
 
                        if (LogLog::isDebugEnabled())
-                       {
-                               LogString debugMsg = LOG4CXX_STR("Checking file 
");
-                               debugMsg.append(canidate_str);
-                               LogLog::debug(debugMsg);
-                       }
+                               LogLog::debug(LOG4CXX_STR("Checking file ") + 
candidate_str);
                        if (candidate.exists(pool))
                        {
-                               LOG4CXX_NS::spi::ConfigurationStatus 
configStatus = tryLoadFile(canidate_str);
-                               if( configStatus == 
LOG4CXX_NS::spi::ConfigurationStatus::Configured ){
-                                       return ResultType{configStatus, 
canidate_str};
+                               std::get<1>(result) = candidate_str;
+                               configure(r);
+                               if (r->isConfigured())
+                               {
+                                       std::get<0>(result) = 
ConfigurationStatus::Configured;
+                                       return result;
                                }
-                               if (LogLog::isDebugEnabled())
-                                       LogLog::debug(LOG4CXX_STR("Unable to 
load file: trying next"));
+                               LogLog::warn(LOG4CXX_STR("Unable to load: ") + 
candidate_str);
                        }
                }
        }
-
-       return ResultType{LOG4CXX_NS::spi::ConfigurationStatus::NotConfigured, 
LogString()};
+       return result;
 }
-
-
-
diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp
index ac448574..93102d9c 100644
--- a/src/main/cpp/domconfigurator.cpp
+++ b/src/main/cpp/domconfigurator.cpp
@@ -65,7 +65,7 @@ using namespace LOG4CXX_NS::rolling;
 
 struct DOMConfigurator::DOMConfiguratorPrivate
 {
-       helpers::Properties props;
+       helpers::Properties props = Configurator::properties();
        spi::LoggerRepositoryPtr repository;
        spi::LoggerFactoryPtr loggerFactory;
 };
diff --git a/src/main/cpp/filewatchdog.cpp b/src/main/cpp/filewatchdog.cpp
index 73ffc732..80d3a3a3 100644
--- a/src/main/cpp/filewatchdog.cpp
+++ b/src/main/cpp/filewatchdog.cpp
@@ -167,4 +167,5 @@ void FileWatchdog::setDelay(long delay1){
 void FileWatchdog::setFile(const File& filename)
 {
     m_priv->file = filename;
+       m_priv->lastModif = 0;
 }
diff --git a/src/main/cpp/optionconverter.cpp b/src/main/cpp/optionconverter.cpp
index f70baef3..178e2437 100644
--- a/src/main/cpp/optionconverter.cpp
+++ b/src/main/cpp/optionconverter.cpp
@@ -228,7 +228,7 @@ LogString OptionConverter::findAndSubst(const LogString& 
key, Properties& props)
 LogString OptionConverter::substVars(const LogString& val, Properties& props)
 {
        LogString sbuf;
-       const logchar delimStartArray[] = { 0x24, 0x7B, 0 };
+       const logchar delimStartArray[] = { 0x24, 0x7B, 0 }; // '$', '{'
        const LogString delimStart(delimStartArray);
        const logchar delimStop = 0x7D; // '}';
        const size_t DELIM_START_LEN = 2;
diff --git a/src/main/cpp/properties.cpp b/src/main/cpp/properties.cpp
index 2c06b0ff..dfa72f83 100644
--- a/src/main/cpp/properties.cpp
+++ b/src/main/cpp/properties.cpp
@@ -399,6 +399,18 @@ Properties::Properties() : properties(new PropertyMap())
 {
 }
 
+Properties::Properties(const Properties& other)
+       : properties(new PropertyMap(*other.properties))
+{
+}
+
+Properties& Properties::operator=(const Properties& other)
+{
+       delete this->properties;
+       this->properties = new PropertyMap(*other.properties);
+       return *this;
+}
+
 Properties::~Properties()
 {
        delete properties;
@@ -451,3 +463,7 @@ std::vector<LogString> Properties::propertyNames() const
        return names;
 }
 
+bool Properties::isEmpty() const
+{
+       return properties->empty();
+}
\ No newline at end of file
diff --git a/src/main/cpp/propertyconfigurator.cpp 
b/src/main/cpp/propertyconfigurator.cpp
index 9586a13b..cf3a555b 100644
--- a/src/main/cpp/propertyconfigurator.cpp
+++ b/src/main/cpp/propertyconfigurator.cpp
@@ -113,7 +113,7 @@ spi::ConfigurationStatus PropertyConfigurator::doConfigure
        auto hierarchy = repository ? repository : 
LogManager::getLoggerRepository();
        hierarchy->setConfigured(true);
 
-       Properties props;
+       Properties props = Configurator::properties();
 
        try
        {
diff --git a/src/main/cpp/system.cpp b/src/main/cpp/system.cpp
index ad4a75f6..423c0136 100644
--- a/src/main/cpp/system.cpp
+++ b/src/main/cpp/system.cpp
@@ -17,13 +17,24 @@
 
 #include <log4cxx/logstring.h>
 #include <log4cxx/helpers/system.h>
+#include <log4cxx/helpers/filesystempath.h>
 
 #include <log4cxx/helpers/transcoder.h>
 #include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/properties.h>
+#include <log4cxx/helpers/loglog.h>
 #include <apr_file_io.h>
 #include <apr_user.h>
 #include <apr_env.h>
 
+#ifdef _WIN32
+#include <windows.h>
+#elif __APPLE__
+#include <mach-o/dyld.h>
+#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+#include <unistd.h> // getpid
+#endif
+#include <sstream>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::helpers;
@@ -120,3 +131,79 @@ LogString System::getProperty(const LogString& lkey)
        return rv;
 }
 
+void System::addProgramFilePathComponents(Properties& props)
+{
+       // Find the executable file name
+       static const int bufSize = 4096;
+       char buf[bufSize+1] = {0}, pathSepar = '/';
+       uint32_t bufCount = 0;
+#if defined(_WIN32)
+       GetModuleFileName(NULL, buf, bufSize);
+       pathSepar = '\\';
+#elif defined(__APPLE__)
+       bufCount = bufSize;
+       _NSGetExecutablePath(buf, &bufCount);
+#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+       std::ostringstream exeLink;
+       exeLink << "/proc/" << getpid() << "/exe";
+       bufCount = readlink(exeLink.str().c_str(), buf, bufSize);
+       if (0 < bufCount)
+               buf[bufCount] = 0;
+#else
+       LogLog::warn(LOG4CXX_STR("Unable to determine the name of the 
executable file on this system"));
+       return;
+#endif
+
+       // Add the path to the properties
+       std::string programFileName(buf);
+       if (programFileName.empty())
+       {
+               LogLog::warn(LOG4CXX_STR("Current executable's file name is 
empty"));
+               return;
+       }
+               
+       LOG4CXX_DECODE_CHAR(lsProgramFileName, programFileName);
+       LogString prefix{ LOG4CXX_STR("PROGRAM_FILE_PATH") };
+       props.setProperty(prefix, lsProgramFileName);
+
+#if LOG4CXX_HAS_FILESYSTEM_PATH
+       // Add the path components to the properties
+       prefix += '.';
+       FilesystemPath programPath(programFileName);
+#if LOG4CXX_LOGCHAR_IS_WCHAR
+       auto root_name = programPath.root_name().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("ROOT_NAME"), root_name);
+       auto root_directory = programPath.root_directory().wstring();
+       props.setProperty(LOG4CXX_STR("ROOT_DIRECTORY"),root_directory);
+       auto root_path = programPath.root_path().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("ROOT_PATH"), root_path);
+       auto relative_path = programPath.relative_path().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("RELATIVE_PATH"), relative_path);
+       auto parent_path = programPath.parent_path().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("PARENT_PATH"), parent_path);
+       auto filename = programPath.filename().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("FILENAME"), filename);
+       auto stem = programPath.stem().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("STEM"), stem);
+       auto extension = programPath.extension().wstring();
+       props.setProperty(prefix + LOG4CXX_STR("EXTENSION"), extension);
+#else
+       LOG4CXX_DECODE_CHAR(root_name, programPath.root_name().string());
+       props.setProperty(prefix + LOG4CXX_STR("ROOT_NAME"), root_name);
+       LOG4CXX_DECODE_CHAR(root_directory, 
programPath.root_directory().string());
+       props.setProperty(LOG4CXX_STR("ROOT_DIRECTORY"),root_directory);
+       LOG4CXX_DECODE_CHAR(root_path, programPath.root_path().string());
+       props.setProperty(prefix + LOG4CXX_STR("ROOT_PATH"), root_path);
+       LOG4CXX_DECODE_CHAR(relative_path, 
programPath.relative_path().string());
+       props.setProperty(prefix + LOG4CXX_STR("RELATIVE_PATH"), relative_path);
+       LOG4CXX_DECODE_CHAR(parent_path, programPath.parent_path().string());
+       props.setProperty(prefix + LOG4CXX_STR("PARENT_PATH"), parent_path);
+       LOG4CXX_DECODE_CHAR(filename, programPath.filename().string());
+       props.setProperty(prefix + LOG4CXX_STR("FILENAME"), filename);
+       LOG4CXX_DECODE_CHAR(stem, programPath.stem().string());
+       props.setProperty(prefix + LOG4CXX_STR("STEM"), stem);
+       LOG4CXX_DECODE_CHAR(extension, programPath.extension().string());
+       props.setProperty(prefix + LOG4CXX_STR("EXTENSION"), extension);
+#endif
+#endif // LOG4CXX_HAS_FILESYSTEM_PATH
+}
diff --git a/src/main/include/log4cxx/defaultconfigurator.h 
b/src/main/include/log4cxx/defaultconfigurator.h
index dabe9b1a..2560c97c 100644
--- a/src/main/include/log4cxx/defaultconfigurator.h
+++ b/src/main/include/log4cxx/defaultconfigurator.h
@@ -39,7 +39,16 @@ class LOG4CXX_EXPORT DefaultConfigurator
                Configure \c repository.
 
                If the configuration file name has not been provided by a call 
to setConfigurationFileName(),
-               the environment variables "LOG4CXX_CONFIGURATION" and 
"log4j.configuration" are examined.
+               the environment variable "LOG4CXX_CONFIGURATION" or 
"log4j.configuration" value is used,
+               with ${varname} instances using either a system environment 
variable value (if found)
+               otherwise using the helpers::Properties object
+               provided by spi::Configurator::properties.
+
+               \usage
+               ~~~
+               setenv 
LOG4CXX_CONFIGURATION="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.xml"
+               ~~~
+
                Unless a custom configurator is specified using the
                "LOG4CXX_CONFIGURATOR_CLASS" or "log4j.configuratorClass"
                environment variable, the PropertyConfigurator will be used to
@@ -57,53 +66,95 @@ class LOG4CXX_EXPORT DefaultConfigurator
                or the environment variables 
"LOG4CXX_CONFIGURATION_WATCH_SECONDS" contains a positive number
                a background thread is started that will periodically check for 
a change to the configuration file
                and apply any configuration changes found.
+
+               Call the spi::LoggerRepository::isConfigured \c repository 
member function
+               to determine whether a configuration file was found.
                */
                static void configure(spi::LoggerRepositoryPtr repository);
 
+               /**
+               Attempt configuration by calling configure() passing the 
default repository.
+
+               See configure() for how the configuration file name is 
determined.
+
+               @return a success indicator.
+               */
+               static spi::ConfigurationStatus tryConfigure();
+
                /**
                Make \c path the configuration file used by configure().
+
+               Any ${varname} instances in the \c path value are expanded
+               using either a system environment variable value (if found)
+               otherwise using the map provided by 
spi::Configurator::properties.
+
+               \usage
+               ~~~{.cpp}
+               
DefaultConfigurator::setConfigurationFileName(LOG4CXX_STR("${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.xml"))
+               ~~~
+
                */
                static void setConfigurationFileName(const LogString& path);
 
                /**
                Make \c seconds the time a background thread will delay before 
checking
                for a change to the configuration file used by configure().
+
+               \usage
+               ~~~{.cpp}
+               DefaultConfigurator::setConfigurationWatchSeconds(1);
+               ~~~
                */
                static void setConfigurationWatchSeconds(int seconds);
 
                /**
-                * Configure Log4cxx from a file.  This method will attempt to 
load the configuration files in the
-                * directories given.
-                *
-                * For example, if we want a configuration file named 
'myapp-logging.xml' with the default location
-                * for this file in /etc/myapp, but to have this overriden by a 
file in /usr/local/etc/myapp, we would
-                * call this function as follows:
-                *
-                * configureFromFile( { "/usr/local/etc/myapp", "/etc/myapp" }, 
{ "myapp-logging.xml" );
+                * Call configure() passing the default repository
+                * after calling setConfigurationFileName() with a path 
composed of
+                * an entry in \c directories and an entry in \c filenames
+                * when the combination identifies an existing file.
                 *
-                * This will then search for files in the following order:
+                \usage
+                ~~~{.cpp}
+                std::vector<LogString> directories
+                    { LOG4CXX_STR(".")
+                    , LOG4CXX_STR("${PROGRAM_FILE_PATH.PARENT_PATH}")
+                    };
+                std::vector<LogString> filenames
+                    { LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.xml")
+                    , LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.properties")
+                    , LOG4CXX_STR("log4cxx.xml")
+                    };
+                DefaultConfigurator::configureFromFile(directories, filenames);
+                ~~~
                 *
-                * <pre>
-                * /usr/local/etc/myapp/myapp-logging.xml
-                * /etc/myapp/myapp-logging.xml
-                * </pre>
+                * Using the above example and an executable file named "myapp"
+                * installed in the  directory "/opt/com.foo/bin",
+                * locations are checked in the following order:
+                * -# "./myapp.xml"
+                * -# "./myapp.properties"
+                * -# "./log4cxx.xml"
+                * -# "/opt/com.foo/bin/myapp.xml"
+                * -# "/opt/com.foo/bin/myapp.properties"
+                * -# "/opt/com.foo/bin/log4cxx.xml"
                 *
-                * The status of configuring Log4cxx as well as the eventual 
filename used is returned.  If a file exists
-                * but it is not able to be used to configure Log4cxx, the next 
file in the list will be tried until
-                * a valid configuration file is found or the end of the list 
is reached.
+                * If a file exists but it is not able to be used to configure 
Log4cxx,
+                * the next file in the combinatorial set will be tried until
+                * a valid configuration file is found or
+                * all values in the combinatorial set have been tried.
                 *
                 * @param directories The directories to look in.
                 * @param filenames The names of the files to look for
-                * @return The status of the configuration, and the filename 
loaded(if a file was found).
+                * @return a success indicator and the configuration file path 
that was used (if found).
                 */
-               static 
std::tuple<LOG4CXX_NS::spi::ConfigurationStatus,LogString> 
configureFromFile(const std::vector<LogString>& directories,
-                                                                               
                                                                                
                 const std::vector<LogString>& filenames);
+               static std::tuple<spi::ConfigurationStatus,LogString> 
configureFromFile
+                       ( const std::vector<LogString>& directories
+                       , const std::vector<LogString>& filenames
+                       );
 
        private:
                static const LogString getConfigurationFileName();
                static const LogString getConfiguratorClass();
                static int getConfigurationWatchDelay();
-               static LOG4CXX_NS::spi::ConfigurationStatus tryLoadFile(const 
LogString& filename);
 
 };      // class DefaultConfigurator
 }  // namespace log4cxx
diff --git a/src/main/cpp/configurator.cpp 
b/src/main/include/log4cxx/helpers/filesystempath.h
similarity index 56%
copy from src/main/cpp/configurator.cpp
copy to src/main/include/log4cxx/helpers/filesystempath.h
index 26fa1461..793bd104 100644
--- a/src/main/cpp/configurator.cpp
+++ b/src/main/include/log4cxx/helpers/filesystempath.h
@@ -15,19 +15,19 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
-#include <log4cxx/spi/configurator.h>
-#include <assert.h>
-#include <log4cxx/logger.h>
+#ifndef LOG4CXX_FILE_SYSTEM_PATH_HDR_
+#define LOG4CXX_FILE_SYSTEM_PATH_HDR_
 
-using namespace LOG4CXX_NS;
-using namespace LOG4CXX_NS::spi;
+#ifdef __has_include                           // Check if __has_include is 
present
+#  if __has_include(<filesystem>)              // Check for a standard version
+#    include <filesystem>
+#    if defined(__cpp_lib_filesystem)          // C++ >= 17
+namespace LOG4CXX_NS { using FilesystemPath = std::filesystem::path; }
+#define LOG4CXX_HAS_FILESYSTEM_PATH 1
+#    endif
+#  else                                        // Not found at all
+#define LOG4CXX_HAS_FILESYSTEM_PATH 0
+#  endif
+#endif // __has_include
 
-IMPLEMENT_LOG4CXX_OBJECT(Configurator)
-
-
-
-
-Configurator::Configurator()
-{
-}
+#endif /* LOG4CXX_FILE_SYSTEM_PATH_HDR_ */
diff --git a/src/main/include/log4cxx/helpers/properties.h 
b/src/main/include/log4cxx/helpers/properties.h
index 7992e841..1481d2cc 100644
--- a/src/main/include/log4cxx/helpers/properties.h
+++ b/src/main/include/log4cxx/helpers/properties.h
@@ -34,18 +34,26 @@ class LOG4CXX_EXPORT Properties
        private:
                typedef std::map<LogString, LogString> PropertyMap;
                PropertyMap* properties;
-               Properties(const Properties&);
-               Properties& operator=(const Properties&);
 
-       public:
+       public: // ...structors
                /**
                 *  Create new instance.
                 */
                Properties();
+               Properties(const Properties&);
+               Properties(const Properties&&) = delete;
+               Properties& operator=(const Properties&);
+               Properties& operator=(const Properties&&) = delete;
                /**
                 * Destructor.
                 */
                ~Properties();
+
+       public: // Methods
+               /** Does this contain any key-value pairs?
+                */
+               bool isEmpty() const;
+
                /**
                Reads a property list (key and element pairs) from the input 
stream.
                The stream is assumed to be using the ISO 8859-1 character 
encoding.
diff --git a/src/main/include/log4cxx/helpers/system.h 
b/src/main/include/log4cxx/helpers/system.h
index 37381b7b..5c0ba3c9 100644
--- a/src/main/include/log4cxx/helpers/system.h
+++ b/src/main/include/log4cxx/helpers/system.h
@@ -34,18 +34,33 @@ class LOG4CXX_EXPORT System
 {
        public:
 
+
                /**
-               Gets the system property indicated by the specified key.
+               Add to \c props the currently executing program file path
+               and the 
[std::filesystem::path](https://en.cppreference.com/w/cpp/filesystem/path.html)
+               decomposition of the currently executing program file path, 
using the variable names:
+               - PROGRAM_FILE_PATH
+               - PROGRAM_FILE_PATH.ROOT_NAME
+               - PROGRAM_FILE_PATH.ROOT_DIRECTORY
+               - PROGRAM_FILE_PATH.ROOT_PATH
+               - PROGRAM_FILE_PATH.RELATIVE_PATH
+               - PROGRAM_FILE_PATH.PARENT_PATH
+               - PROGRAM_FILE_PATH.FILENAME
+               - PROGRAM_FILE_PATH.STEM
+               - PROGRAM_FILE_PATH.EXTENSION
+               */
+               static void addProgramFilePathComponents(Properties& props);
+
+               /**
+               The value of the system property associated with \c key.
 
                @param key the name of the system property.
 
-               @return the string value of the system property, or the default 
value if
-               there is no property with that key.
+               @return the string value of the system property.
 
                @throws IllegalArgumentException if key is empty.
                */
                static LogString getProperty(const LogString& key);
-
 };
 } // namespace helpers
 } //  namespace log4cxx
diff --git a/src/main/include/log4cxx/private/log4cxx_private.h.in 
b/src/main/include/log4cxx/private/log4cxx_private.h.in
index b7bed0be..c4322a31 100644
--- a/src/main/include/log4cxx/private/log4cxx_private.h.in
+++ b/src/main/include/log4cxx/private/log4cxx_private.h.in
@@ -63,4 +63,5 @@
 #define LOG4CXX_HAS_PTHREAD_GETNAME @HAS_PTHREAD_GETNAME@
 #define LOG4CXX_HAS_THREAD_LOCAL @HAS_THREAD_LOCAL@
 
+#define LOG4CXX_EXPAND_CONFIG_ENV_VAR @EXPAND_CONFIG_ENV_VAR@
 #endif
diff --git a/src/main/include/log4cxx/spi/configurator.h 
b/src/main/include/log4cxx/spi/configurator.h
index d4dae180..651a311d 100644
--- a/src/main/include/log4cxx/spi/configurator.h
+++ b/src/main/include/log4cxx/spi/configurator.h
@@ -19,6 +19,7 @@
 #define _LOG4CXX_SPI_CONFIGURATOR_H
 
 #include <log4cxx/spi/loggerrepository.h>
+#include <log4cxx/helpers/properties.h>
 
 namespace LOG4CXX_NS
 {
@@ -63,6 +64,24 @@ class LOG4CXX_EXPORT Configurator : virtual public 
helpers::Object
 #endif
                        ) = 0;
 
+               /**
+               The key value pairs used when expanding ${varname} instances in 
a configuration file.
+
+               By default, the map holds the currently executing program file 
path
+               and the 
[std::filesystem::path](https://en.cppreference.com/w/cpp/filesystem/path.html)
+               decomposition of the currently executing program file path, 
using the variable names:
+               - PROGRAM_FILE_PATH
+               - PROGRAM_FILE_PATH.ROOT_NAME
+               - PROGRAM_FILE_PATH.ROOT_DIRECTORY
+               - PROGRAM_FILE_PATH.ROOT_PATH
+               - PROGRAM_FILE_PATH.RELATIVE_PATH
+               - PROGRAM_FILE_PATH.PARENT_PATH
+               - PROGRAM_FILE_PATH.FILENAME
+               - PROGRAM_FILE_PATH.STEM
+               - PROGRAM_FILE_PATH.EXTENSION
+               */
+               static helpers::Properties& properties();
+
        protected:
                Configurator();
 
diff --git a/src/site/markdown/change-report-gh.md 
b/src/site/markdown/change-report-gh.md
index 9cffd615..912f43a2 100644
--- a/src/site/markdown/change-report-gh.md
+++ b/src/site/markdown/change-report-gh.md
@@ -25,6 +25,7 @@ Change Log {#changelog}
 
 | Version             | Date       | Description          |
 | ------------------- | ---------- | -------------------- |
+| [1.6.0](#rel_1_6_0) | 2025-XX-XX | Maintenance release  |
 | [1.5.0](#rel_1_5_0) | 2025-08-03 | Maintenance release  |
 | [1.4.0](#rel_1_4_0) | 2025-03-01 | Maintenance release  |
 | [1.3.1](#rel_1_3_1) | 2024-11-30 | Bugfix release       |
@@ -49,6 +50,21 @@ Change Log {#changelog}
 | [0.1.0](#rel_1_0)   | 2003-07-08 |                      |
 | [0.0.1](#rel_0_1)   | 2003-05-31 |                      |
 
+## Release 1.6.0 - 2025-XX-XX {#rel_1_6_0}
+
+Release 1.6.0 includes the following new features:
+
+Release 1.6.0 includes the following new features:
+
+* Configuration ${varname} values can be set programatically prior to loading 
a configuration file (see \ref com/foo/config4.cpp)
+   \[[#520](https://github.com/apache/logging-log4cxx/pull/520)\]
+* The current executable's file name and its components are available for use 
in a configuration file
+and the LOG4CXX_CONFIGURATION environment variable (see 
log4cxx::spi::Configurator::properties).
+   \[[#520](https://github.com/apache/logging-log4cxx/pull/520)\]
+
+The following issues have been addressed:
+
+
 ## Release 1.5.0 - 2025-08-03 {#rel_1_5_0}
 
 Release 1.5.0 includes the following new features:
diff --git a/src/site/markdown/example-programs.md 
b/src/site/markdown/example-programs.md
index 72992d80..5ad3d527 100644
--- a/src/site/markdown/example-programs.md
+++ b/src/site/markdown/example-programs.md
@@ -115,7 +115,7 @@ This version of *config.cpp* instructs 
[PropertyConfigurator](@ref log4cxx.Prope
 to use the *MyApp.properties* file to configure Log4cxx.
 A more realistic approach would (for example)
 use the current module name to select the configuration file
-(see the \ref com/foo/config3.cpp file for how to do this).
+(see the \ref com/foo/config4.cpp file for how to do this).
 
 Here is a sample *MyApp.properties* configuration file that results in exactly 
same output
 as the previous [BasicConfigurator::configure](@ref 
log4cxx.BasicConfigurator.configure) based example.
@@ -196,3 +196,6 @@ This file is a simplified example of encapsulated Log4cxx 
configuration.
 
 \example com/foo/config3.cpp
 This file is an example of how to use the current module name to select the 
Log4cxx configuration file.
+
+\example com/foo/config4.cpp
+This file is a simpler example of how to use the current module name to select 
the Log4cxx configuration file.
\ No newline at end of file
diff --git a/src/test/cpp/autoconfiguretestcase.cpp 
b/src/test/cpp/autoconfiguretestcase.cpp
index e486727e..da634bee 100644
--- a/src/test/cpp/autoconfiguretestcase.cpp
+++ b/src/test/cpp/autoconfiguretestcase.cpp
@@ -19,11 +19,15 @@
 #include <log4cxx/logmanager.h>
 #include <log4cxx/defaultconfigurator.h>
 #include <log4cxx/helpers/bytebuffer.h>
+#include <log4cxx/helpers/exception.h>
 #include <log4cxx/helpers/fileinputstream.h>
 #include <log4cxx/helpers/fileoutputstream.h>
 #include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/optionconverter.h>
 #include <log4cxx/helpers/pool.h>
 #include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/filesystempath.h>
+#include <log4cxx/helpers/transcoder.h>
 #include <thread>
 #include <apr_file_io.h>
 #include <apr_file_info.h>
@@ -53,11 +57,8 @@ using namespace log4cxx;
 LOGUNIT_CLASS(AutoConfigureTestCase)
 {
        LOGUNIT_TEST_SUITE(AutoConfigureTestCase);
-       LOGUNIT_TEST(copyPropertyFile);
-       LOGUNIT_TEST_THREADS(test1, 4);
+       LOGUNIT_TEST(test1);
        LOGUNIT_TEST(test2);
-       LOGUNIT_TEST(test3);
-       LOGUNIT_TEST(shutdown);
        LOGUNIT_TEST_SUITE_END();
 #ifdef _DEBUG
        struct Fixture
@@ -66,53 +67,75 @@ LOGUNIT_CLASS(AutoConfigureTestCase)
                        helpers::LogLog::setInternalDebugging(true);
                }
        } suiteFixture;
-       apr_time_t m_initTime = apr_time_now();
 #endif
        helpers::Pool m_pool;
        char m_buf[2048];
-       LogString m_configFile = 
LOG4CXX_STR("autoconfiguretestcase.properties");
+       LogString m_configFile;
+       spi::ConfigurationStatus m_status;
 public:
 
-       void copyPropertyFile()
+       void copyPropertyFile(const LogString& lsDestDir, const LogString& 
lsFileName)
        {
-               
LOGUNIT_ASSERT(File(LOG4CXX_STR("input/autoConfigureTest.properties")).exists(m_pool));
-               LOGUNIT_ASSERT(apr_file_copy
-                       ( "input/autoConfigureTest.properties"
-                       , "autoconfiguretestcase.properties"
-                       , APR_FPROT_UREAD | APR_FPROT_UWRITE
-                       , m_pool.getAPRPool()
-                       ) == APR_SUCCESS);
+               LOG4CXX_ENCODE_CHAR(destDir, lsDestDir);
+               LOG4CXX_ENCODE_CHAR(fileName, lsFileName);
+               auto status = apr_file_copy
+                  ( "input/autoConfigureTest.properties"
+                  , (destDir + "/" + fileName + ".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("/") + lsFileName + LOG4CXX_STR(".properties"), status));
+       }
 
-               DefaultConfigurator::setConfigurationFileName(m_configFile);
+       void setUp()
+       {
+#if LOG4CXX_HAS_FILESYSTEM_PATH
+               auto fileName = 
spi::Configurator::properties().getProperty(LOG4CXX_STR("PROGRAM_FILE_PATH.STEM"));
+#else
+               LogString fileName = LOG4CXX_STR("autoconfiguretestcase");
+#endif
+               auto lsTempDir = 
helpers::OptionConverter::getSystemProperty(LOG4CXX_STR("TEMP"), 
LOG4CXX_STR("/tmp"));
+               copyPropertyFile(lsTempDir, fileName);
                DefaultConfigurator::setConfigurationWatchSeconds(1);
-               LOGUNIT_ASSERT(File(m_configFile).exists(m_pool));
+               std::vector<LogString> paths
+                       { lsTempDir
+                       };
+               std::vector<LogString> names
+#if LOG4CXX_HAS_FILESYSTEM_PATH
+                       { LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.properties")
+#else
+                       { LOG4CXX_STR("autoconfiguretestcase.properties")
+#endif
+                       };
+               std::tie(m_status, m_configFile) = 
DefaultConfigurator::configureFromFile(paths, names);
        }
 
-       void shutdown()
+       void tearDown()
        {
                LogManager::shutdown();
-               
LOGUNIT_ASSERT(apr_file_remove("autoconfiguretestcase.properties", 
m_pool.getAPRPool()) == APR_SUCCESS);
+               LOG4CXX_ENCODE_CHAR(configFile, m_configFile);
+               apr_file_remove(configFile.c_str(), m_pool.getAPRPool());
+               // wait 0.2 sec to ensure the file is really gone on Windows
+               apr_sleep(200000);
        }
 
        void test1()    
        {
-               auto debugLogger = 
LogManager::getLogger(LOG4CXX_STR("AutoConfig.test1"));
-               LOGUNIT_ASSERT(debugLogger);
-               LOGUNIT_ASSERT(!debugLogger->isDebugEnabled());
-               auto rep = LogManager::getLoggerRepository();
-               LOGUNIT_ASSERT(rep);
-               LOGUNIT_ASSERT(rep->isConfigured());
+               LOGUNIT_ASSERT_EQUAL(m_status, 
spi::ConfigurationStatus::Configured);
+               LOGUNIT_ASSERT(File(m_configFile).exists(m_pool));
+               auto debugLogger1 = 
LogManager::getLogger(LOG4CXX_STR("AutoConfig.test1"));
+               LOGUNIT_ASSERT(debugLogger1);
+               LOGUNIT_ASSERT(!debugLogger1->isDebugEnabled());
+               auto debugLogger2 = 
LogManager::getLogger(LOG4CXX_STR("AutoConfig.test2"));
+               LOGUNIT_ASSERT(debugLogger2);
+               LOGUNIT_ASSERT(debugLogger2->isDebugEnabled());
        }
 
        void test2()
        {
-               auto debugLogger = 
LogManager::getLogger(LOG4CXX_STR("AutoConfig.test2"));
-               LOGUNIT_ASSERT(debugLogger);
-               LOGUNIT_ASSERT(debugLogger->isDebugEnabled());
-       }
-
-       void test3()
-       {
+               LOGUNIT_ASSERT_EQUAL(m_status, 
spi::ConfigurationStatus::Configured);
+               LOGUNIT_ASSERT(File(m_configFile).exists(m_pool));
                // wait 2 sec to ensure the modification time is different to 
that held in the WatchDog
                apr_sleep(2000000);
                auto debugLogger = 
LogManager::getLogger(LOG4CXX_STR("AutoConfig.test3"));
diff --git a/src/test/cpp/rolling/multiprocessrollingtest.cpp 
b/src/test/cpp/rolling/multiprocessrollingtest.cpp
index 244b12a6..91356f3d 100644
--- a/src/test/cpp/rolling/multiprocessrollingtest.cpp
+++ b/src/test/cpp/rolling/multiprocessrollingtest.cpp
@@ -32,16 +32,6 @@
 #include <fstream>
 #include <apr_thread_proc.h>
 
-#ifdef _WIN32
-#include <windows.h> // GetModuleFileName
-#elif __APPLE__
-#include <mach-o/dyld.h> // _NSGetExecutablePath
-#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
-#include <unistd.h> // getpid
-#else
-#include <cstring> // strncpy
-#endif
-
 using namespace LOG4CXX_NS;
 
 auto getLogger(const std::string& name) -> LoggerPtr {
@@ -365,23 +355,9 @@ private:
 
        std::string GetExecutableFileName()
        {
-               static const int bufSize = 4096;
-               char buf[bufSize+1] = {0};
-               [[maybe_unused]] uint32_t bufCount = bufSize;
-#if defined(_WIN32)
-               GetModuleFileName(NULL, buf, bufSize);  // TODO handle failure, 
e.g. ERROR_INSUFFICIENT_BUFFER
-#elif defined(__APPLE__)
-               _NSGetExecutablePath(buf, &bufCount);  // TODO handle failure
-#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || 
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
-               std::ostringstream exeLink;
-               exeLink << "/proc/" << getpid() << "/exe";
-               bufCount = readlink(exeLink.str().c_str(), buf, bufSize);
-               if (0 < bufCount)
-                       buf[bufCount] = 0;
-#else
-               strncpy(buf, "multiprocessrollingtest", bufSize);
-#endif
-               return std::string(buf);
+               auto lsProgramFilePath = 
spi::Configurator::properties().getProperty(LOG4CXX_STR("PROGRAM_FILE_PATH"));
+               LOG4CXX_ENCODE_CHAR(programFilePath, lsProgramFilePath);
+               return programFilePath;
        }
 #ifdef _DEBUG
        struct Fixture

Reply via email to