Skip to content

Commit d1257fd

Browse files
authored
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
1 parent 1cbd3c6 commit d1257fd

27 files changed

+558
-169
lines changed

.github/workflows/log4cxx-macos.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ jobs:
5656
if [ ${{ matrix.os }} != macos-13 ]; then brew install apr-util; fi
5757
if [ ${{ matrix.odbc }} == ON ]; then brew install unixodbc; fi
5858
if [ ${{ matrix.qt }} == ON ]; then brew install qt; fi
59+
if [ ${{ matrix.cxx }} == "g++-14" -a $(cmake --version | head -1 | awk '{split($3, a, "."); print a[1]}') -lt 4 ]
60+
then brew install cmake ninja
61+
fi
5962
6063
- name: 'configure and build'
6164
run: |

.github/workflows/log4cxx-windows-static.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
with:
5151
repository: microsoft/vcpkg
5252
path: vcpkg
53-
ref: 2024.01.12
53+
ref: 2025.07.25
5454

5555
- name: 'Configure Dependencies'
5656
if: steps.restore-vcpkg-cache.outputs.cache-hit != 'true'

.github/workflows/log4cxx-windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
with:
5151
repository: microsoft/vcpkg
5252
path: vcpkg
53-
ref: 2024.01.12
53+
ref: 2025.07.25
5454

5555
- name: 'Configure Dependencies'
5656
if: steps.restore-vcpkg-cache.outputs.cache-hit != 'true'

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ else()
109109
set(ENABLE_FMT_LAYOUT "OFF")
110110
endif()
111111

112+
option(LOG4CXX_CONFIG_VAR_EXPANSION "Expand \${varname} instances in LOG4CXX_CONFIGURATION" ON)
113+
if(LOG4CXX_CONFIG_VAR_EXPANSION)
114+
set(EXPAND_CONFIG_ENV_VAR 1)
115+
else()
116+
set(EXPAND_CONFIG_ENV_VAR 0)
117+
endif(LOG4CXX_CONFIG_VAR_EXPANSION)
118+
112119
# Request C++20, if available
113120
# This *should* fallback to an older standard if it is not available
114121
if( NOT "${CMAKE_CXX_STANDARD}")

src/examples/cpp/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES)
4646
target_link_libraries(${PROGRAM_NAME} PRIVATE log4cxx-qt)
4747
endif()
4848
if(${exampleName} STREQUAL auto-configured)
49-
target_sources(${PROGRAM_NAME} PRIVATE com/foo/config3.cpp )
49+
target_sources(${PROGRAM_NAME} PRIVATE com/foo/config4.cpp )
5050
endif()
5151
target_compile_definitions(${PROGRAM_NAME} PRIVATE ${EXAMPLE_COMPILE_DEFINITIONS} ${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS} ${APR_UTIL_COMPILE_DEFINITIONS} )
5252
target_include_directories(${PROGRAM_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR} $<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)

src/examples/cpp/auto-configured.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
44
<param name="Target" value="System.out"/>
55
<layout class="org.apache.log4j.PatternLayout">
6-
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss}] %c %-5p - %m%n"/>
6+
<param name="ConversionPattern" value="${CURRENT_VENDOR_FOLDER}.${CURRENT_PRODUCT_FOLDER}: [%d{yyyy-MM-dd HH:mm:ss}] %c %-5p - %m%n"/>
77
</layout>
88
</appender>
99

src/examples/cpp/com/foo/config3.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ auto DefaultConfigurationFileNames(std::string& altPrefix) -> std::vector<std::s
5252
GetModuleFileName(NULL, buf, bufSize);
5353
pathSepar = '\\';
5454
#elif defined(__APPLE__)
55+
bufCount = bufSize;
5556
_NSGetExecutablePath(buf, &bufCount);
5657
#elif (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
5758
std::ostringstream exeLink;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "config.h"
18+
#include "product_version.h" // Provides getVendorFolder() and getProductFolder()
19+
#include <log4cxx/logmanager.h>
20+
#include <log4cxx/defaultconfigurator.h>
21+
#include <log4cxx/basicconfigurator.h>
22+
#include <log4cxx/helpers/transcoder.h>
23+
#include <vector>
24+
25+
namespace com { namespace foo {
26+
27+
// Retrieve the \c name logger pointer.
28+
// Configure Log4cxx on the first call.
29+
auto getLogger(const std::string& name) -> LoggerPtr {
30+
using namespace log4cxx;
31+
static struct log4cxx_initializer {
32+
log4cxx_initializer() {
33+
auto vendorFolder = getVendorFolder();
34+
auto productFolder = getProductFolder();
35+
LOG4CXX_DECODE_CHAR(lsVendorFolder, vendorFolder);
36+
LOG4CXX_DECODE_CHAR(lsProductFolder, productFolder);
37+
38+
// Allow expansion of ${CURRENT_VENDOR_FOLDER} and ${CURRENT_PRODUCT_FOLDER}
39+
// when loading a configuration from a file
40+
auto& props = spi::Configurator::properties();
41+
props.setProperty(LOG4CXX_STR("CURRENT_VENDOR_FOLDER"), lsVendorFolder);
42+
props.setProperty(LOG4CXX_STR("CURRENT_PRODUCT_FOLDER"), lsProductFolder);
43+
44+
// Check every 5 seconds for configuration file changes
45+
DefaultConfigurator::setConfigurationWatchSeconds(5);
46+
47+
// Look for a configuration file in the current working directory
48+
// and the same directory as the program
49+
std::vector<LogString> paths
50+
{ LOG4CXX_STR(".")
51+
, LOG4CXX_STR("${PROGRAM_FILE_PATH.PARENT_PATH}")
52+
};
53+
std::vector<LogString> names
54+
{ LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.xml")
55+
, LOG4CXX_STR("${PROGRAM_FILE_PATH.STEM}.properties")
56+
};
57+
auto status = spi::ConfigurationStatus::NotConfigured;
58+
auto selectedPath = LogString();
59+
std::tie(status, selectedPath) = DefaultConfigurator::configureFromFile(paths, names);
60+
if (status == spi::ConfigurationStatus::NotConfigured)
61+
BasicConfigurator::configure(); // Send events to the console
62+
}
63+
~log4cxx_initializer() {
64+
LogManager::shutdown();
65+
}
66+
} initialiser;
67+
return name.empty()
68+
? LogManager::getRootLogger()
69+
: LogManager::getLogger(name);
70+
}
71+
72+
} } // namespace com::foo
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
std::string getVendorFolder()
19+
{
20+
return "ApacheSoftwareFoundation";
21+
}
22+
std::string getProductFolder()
23+
{
24+
return "Logging";
25+
}

src/main/cpp/configurator.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,44 @@
1919
#include <log4cxx/spi/configurator.h>
2020
#include <assert.h>
2121
#include <log4cxx/logger.h>
22+
#include <log4cxx/helpers/singletonholder.h>
23+
#include <log4cxx/helpers/system.h>
24+
25+
#if !defined(LOG4CXX)
26+
#define LOG4CXX 1
27+
#endif
28+
#include <log4cxx/helpers/aprinitializer.h>
2229

2330
using namespace LOG4CXX_NS;
2431
using namespace LOG4CXX_NS::spi;
2532

2633
IMPLEMENT_LOG4CXX_OBJECT(Configurator)
2734

2835

36+
namespace
37+
{
38+
struct ConfiguratorTag {};
39+
using ConfiguratorProperties = std::pair<ConfiguratorTag, helpers::Properties>;
2940

41+
ConfiguratorProperties& getInstance()
42+
{
43+
using ConfiguratorObject = helpers::SingletonHolder<ConfiguratorProperties>;
44+
auto result = helpers::APRInitializer::getOrAddUnique<ConfiguratorObject>
45+
( []() -> helpers::ObjectPtr
46+
{ return std::make_shared<ConfiguratorObject>(); }
47+
);
48+
return result->value();
49+
}
50+
}
3051

3152
Configurator::Configurator()
3253
{
3354
}
55+
56+
helpers::Properties& Configurator::properties()
57+
{
58+
auto& result = getInstance().second;
59+
if (result.isEmpty())
60+
helpers::System::addProgramFilePathComponents(result);
61+
return result;
62+
}

0 commit comments

Comments
 (0)