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 f11f6fc6 Using default configuration, BufferedIO and a watchdog caused
recursive configuration (#739)
f11f6fc6 is described below
commit f11f6fc6b8c98dd57bf3e173e1ba3e4f527ce4ce
Author: Stephen Webb <[email protected]>
AuthorDate: Wed Aug 26 13:10:23 2026 +1000
Using default configuration, BufferedIO and a watchdog caused recursive
configuration (#739)
* Stop the watchdog from loading the configuration file twice
* Prevent internal logger creation from triggering default configuration
---
src/main/cpp/filewatchdog.cpp | 9 +-
src/main/cpp/hierarchy.cpp | 2 +-
src/main/cpp/loglog.cpp | 6 ++
src/main/cpp/threadutility.cpp | 2 +-
src/main/include/log4cxx/helpers/loglog.h | 5 ++
src/test/cpp/CMakeLists.txt | 1 +
src/test/cpp/autoconfiguretestcase.cpp | 4 +-
src/test/cpp/defaultconfiguratortest.cpp | 97 ++++++++++++++++++++++
.../resources/input/defaultconfiguratortest.xml | 28 +++++++
9 files changed, 147 insertions(+), 7 deletions(-)
diff --git a/src/main/cpp/filewatchdog.cpp b/src/main/cpp/filewatchdog.cpp
index efbc1fea..66c7a482 100644
--- a/src/main/cpp/filewatchdog.cpp
+++ b/src/main/cpp/filewatchdog.cpp
@@ -162,8 +162,11 @@ void FileWatchdog::setDelay(long delay1){
}
}
-void FileWatchdog::setFile(const File& filename)
+void FileWatchdog::setFile(const File& newValue)
{
- m_priv->file = filename;
- m_priv->lastModif = 0;
+ if (m_priv->file.getPath() != newValue.getPath())
+ {
+ m_priv->file = newValue;
+ m_priv->lastModif = 0;
+ }
}
diff --git a/src/main/cpp/hierarchy.cpp b/src/main/cpp/hierarchy.cpp
index 60142105..4b7103e2 100644
--- a/src/main/cpp/hierarchy.cpp
+++ b/src/main/cpp/hierarchy.cpp
@@ -307,8 +307,8 @@ void Hierarchy::ensureIsConfigured(std::function<void()>
configurator)
std::lock_guard<std::recursive_mutex> lock(m_priv->configuredMutex);
if (!m_priv->configured && m_priv->alreadyTriedMethod !=
configurator.target_type().name())
{
- configurator();
m_priv->alreadyTriedMethod = configurator.target_type().name();
+ configurator();
}
}
diff --git a/src/main/cpp/loglog.cpp b/src/main/cpp/loglog.cpp
index 1f8875fe..3aab3dac 100644
--- a/src/main/cpp/loglog.cpp
+++ b/src/main/cpp/loglog.cpp
@@ -17,6 +17,7 @@
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/logmanager.h>
#include <log4cxx/helpers/transcoder.h>
#include <iostream>
#if !defined(LOG4CXX)
@@ -187,6 +188,11 @@ void LogLog::error(const LogString& msg, const
std::exception& e)
}
}
+LoggerPtr LogLog::getLogger(const LogString& name)
+{
+ return LogManager::getLoggerRepository()->getLogger(name);
+}
+
#if !LOG4CXX_LOGCHAR_IS_UTF8
void LogLog::trace(const LoggerPtr& category, const std::string& msg)
{
diff --git a/src/main/cpp/threadutility.cpp b/src/main/cpp/threadutility.cpp
index 4ea4c078..230cad8d 100644
--- a/src/main/cpp/threadutility.cpp
+++ b/src/main/cpp/threadutility.cpp
@@ -270,7 +270,7 @@ ThreadStartPost ThreadUtility::postStartFunction()
void ThreadUtility::addPeriodicTask(const LogString& name,
std::function<void()> f, const Period& delay)
{
if (!m_priv->log)
- m_priv->log = Logger::getLogger("ThreadUtility");
+ m_priv->log = LogLog::getLogger(LOG4CXX_STR("ThreadUtility"));
LOGLOG_DEBUG(m_priv->log, LOG4CXX_STR("addPeriodicTask: ") << name);
std::lock_guard<std::recursive_mutex> lock(m_priv->job_mutex);
if (m_priv->maxDelay < delay)
diff --git a/src/main/include/log4cxx/helpers/loglog.h
b/src/main/include/log4cxx/helpers/loglog.h
index 82fde47d..bd64f792 100644
--- a/src/main/include/log4cxx/helpers/loglog.h
+++ b/src/main/include/log4cxx/helpers/loglog.h
@@ -132,6 +132,11 @@ class LOG4CXX_EXPORT LogLog
*/
static void warn(const LogString& msg, const std::exception&
ex);
+ /**
+ A \c name category for internal logging.
+ */
+ static LoggerPtr getLogger(const LogString& name);
+
private:
static void emit_log(const LogString& prefix, const LogString&
msg, const LogString& suffix);
static void emit_log(const LogString& prefix, const
std::exception& ex, const LogString& suffix);
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 913a5512..e3d82518 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -49,6 +49,7 @@ set(ALL_LOG4CXX_TESTS
asyncappendertestcase
asyncappenderracestress
consoleappendertestcase
+ defaultconfiguratortest
decodingtest
encodingtest
fileappendertest
diff --git a/src/test/cpp/autoconfiguretestcase.cpp
b/src/test/cpp/autoconfiguretestcase.cpp
index af3fe15e..38336738 100644
--- a/src/test/cpp/autoconfiguretestcase.cpp
+++ b/src/test/cpp/autoconfiguretestcase.cpp
@@ -113,8 +113,8 @@ public:
LogManager::shutdown();
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);
+ // wait 1 sec to ensure the WatchDog will reload the
configuration
+ apr_sleep(1000000);
}
void testSetup()
diff --git a/src/test/cpp/defaultconfiguratortest.cpp
b/src/test/cpp/defaultconfiguratortest.cpp
new file mode 100644
index 00000000..6e93ad9a
--- /dev/null
+++ b/src/test/cpp/defaultconfiguratortest.cpp
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "logunit.h"
+#include <log4cxx/logmanager.h>
+#include <log4cxx/defaultconfigurator.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/fileappender.h>
+#include <log4cxx/helpers/filesystempath.h>
+
+using namespace LOG4CXX_NS;
+
+namespace
+{
+ auto GetLogger(const std::string& name) -> LoggerPtr
+ {
+ static struct log4cxx_initializer
+ {
+ log4cxx_initializer()
+ {
+ // Check every 5 seconds for configuration file
changes
+
DefaultConfigurator::setConfigurationWatchSeconds(5);
+#if !LOG4CXX_HAS_FILESYSTEM_PATH
+ auto& props = spi::Configurator::properties();
+
props.setProperty(LOG4CXX_STR("PROGRAM_FILE_PATH.STEM"),
LOG4CXX_STR("defaultconfiguratortest"));
+
props.setProperty(LOG4CXX_STR("PROGRAM_FILE_PATH.PARENT_PATH"),
LOG4CXX_STR("output"));
+#endif
+
+
+ // Look for a configuration file in the current
working directory
+ // and the same directory as the program
+ std::vector<LogString> paths
+ { LOG4CXX_STR("input")
+ ,
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);
+ }
+
+ auto logger = GetLogger("com.test");
+}
+
+LOGUNIT_CLASS(DefaultConfiguratorTest)
+{
+ LOGUNIT_TEST_SUITE(DefaultConfiguratorTest);
+ LOGUNIT_TEST(test1);
+ LOGUNIT_TEST_SUITE_END();
+public:
+
+ void test1()
+ {
+ LOGUNIT_ASSERT(logger);
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("com.test"),
logger->getName());
+ auto comLogger = logger->getParent();
+ LOGUNIT_ASSERT(comLogger);
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("com"), comLogger->getName());
+ auto rootLogger = comLogger->getParent();
+ LOGUNIT_ASSERT(rootLogger);
+ LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("root"),
rootLogger->getName());
+ auto appender = rootLogger->getAppender(LOG4CXX_STR("A1"));
+ LOGUNIT_ASSERT(appender);
+ auto fileAppender = LOG4CXX_NS::cast<FileAppender>(appender);
+ LOGUNIT_ASSERT(fileAppender);
+ LOGUNIT_ASSERT(fileAppender->getBufferedIO());
+ }
+};
+
+LOGUNIT_TEST_SUITE_REGISTRATION(DefaultConfiguratorTest);
diff --git a/src/test/resources/input/defaultconfiguratortest.xml
b/src/test/resources/input/defaultconfiguratortest.xml
new file mode 100644
index 00000000..5c1a4a15
--- /dev/null
+++ b/src/test/resources/input/defaultconfiguratortest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="true">
+
+ <appender name="A1" class="org.apache.log4j.RollingFileAppender">
+ <param name="file"
value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.log" />
+ <param name="BufferedIO" value="true" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %c
%-5p - %m%n" />
+ </layout>
+ <rollingPolicy class="org.apache.log4j.rolling.FixedWindowRollingPolicy">
+ <param name="fileNamePattern"
value="${PROGRAM_FILE_PATH.PARENT_PATH}/${PROGRAM_FILE_PATH.STEM}.%i.log"/>
+ <param name="minIndex" value="0"/>
+ </rollingPolicy>
+ <triggeringPolicy
class="org.apache.log4j.rolling.SizeBasedTriggeringPolicy">
+ <param name="maxFileSize" value="4MB" />
+ </triggeringPolicy>
+ </appender>
+
+ <root>
+ <priority value="info" />
+ <appender-ref ref="A1"/>
+ </root>
+
+ <logger name="com" >
+ <priority value="debug"/>
+ </logger>
+
+</log4j:configuration>
\ No newline at end of file