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