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 <[email protected]>
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