This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch next_stable
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
The following commit(s) were added to refs/heads/next_stable by this push:
new 2ac4b90e Add example of logging in a static initializer (#141)
2ac4b90e is described below
commit 2ac4b90e8b9fc311d9cba1dbbdae5e151469c843
Author: Stephen Webb <[email protected]>
AuthorDate: Sun Oct 9 15:28:10 2022 +1100
Add example of logging in a static initializer (#141)
* Replace the description default configuration behaviour in the usage page
with a link to the DefaultConfigurator documentation
* Improve automatic configuration explainations
* Add an 'auto-configured' example showing logging during static
initialization
* Link to the auto-confiure example from the usage page. Update the logging
performance section.
* Enable Log::Log debug messages in a Debug build of the 'auto-configured'
example
Co-authored-by: Stephen Webb <[email protected]>
---
src/cmake/win32_target_environment_path.cmake | 27 ++++
src/examples/cpp/CMakeLists.txt | 29 +++-
src/examples/cpp/UserLib/logmanager.cpp | 175 +++++++++++++++++++++++++
src/examples/cpp/UserLib/logmanager.h | 8 ++
src/examples/cpp/auto-configured.cpp | 37 ++++++
src/examples/cpp/auto-configured.xml | 15 +++
src/main/cpp/defaultconfigurator.cpp | 4 +-
src/main/include/log4cxx/defaultconfigurator.h | 18 ++-
src/main/include/log4cxx/hierarchy.h | 14 +-
src/main/include/log4cxx/logmanager.h | 133 +++++++++++--------
src/site/markdown/usage.md | 83 ++++++------
src/test/cpp/CMakeLists.txt | 24 +---
src/test/cpp/autoconfiguretestcase.cpp | 6 +-
13 files changed, 430 insertions(+), 143 deletions(-)
diff --git a/src/cmake/win32_target_environment_path.cmake
b/src/cmake/win32_target_environment_path.cmake
new file mode 100644
index 00000000..ea122c89
--- /dev/null
+++ b/src/cmake/win32_target_environment_path.cmake
@@ -0,0 +1,27 @@
+# Put the list of runtime path directories into varName.
+function(get_target_environment_path varName)
+ get_filename_component(APR_DLL_DIR "${APR_DLL}" DIRECTORY)
+ get_filename_component(APR_UTIL_DLL_DIR "${APR_UTIL_DLL}" DIRECTORY)
+ get_filename_component(EXPAT_LIB_DIR "${EXPAT_LIBRARY}" DIRECTORY)
+
+
+ set(EXPAT_DLL_DIR "${EXPAT_LIB_DIR}/../bin")
+ set(LOG4CXX_DLL_DIR "$<SHELL_PATH:$<TARGET_FILE_DIR:log4cxx>>;")
+ set(PATH_FOR_TESTS
${CMAKE_PROGRAM_PATH};${APR_DLL_DIR};${APR_UTIL_DLL_DIR};${LOG4CXX_DLL_DIR};${EXPAT_DLL_DIR}\;)
+ list(REMOVE_DUPLICATES PATH_FOR_TESTS)
+
+ # Note: we need to include the APR DLLs on our path so that the tests will
run.
+ # The way that CMake sets the environment is that it actually generates a
secondary file,
+ # CTestTestfile.cmake, which sets the final properties of the test.
+ # However, this results in a secondary quirk to the running of the tests:
CMake uses
+ # a semicolon to deliminate entries in a list! Since the Windows PATH is
semicolon-delimited
+ # as well, CMake uses only the first entry in the list when setting the path.
+ # So, we need to do a triple escape on the PATH that we want to set in order
for CMake to
+ # properly interpret the PATH
+ set(NORMAL_PATH $ENV{PATH})
+ set(ESCAPED_PATH "")
+ foreach( ENTRY ${PATH_FOR_TESTS}${NORMAL_PATH} )
+ set(ESCAPED_PATH "${ESCAPED_PATH}${ENTRY}\\\;")
+ endforeach()
+ set(${varName} ${ESCAPED_PATH} PARENT_SCOPE)
+endfunction()
diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index cfb2fe7a..0d36048f 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -15,13 +15,33 @@
# limitations under the License.
#
-set(ALL_LOG4CXX_EXAMPLES console delayedloop stream trivial custom-appender)
+set(ALL_LOG4CXX_EXAMPLES auto-configured console delayedloop stream trivial
custom-appender)
+if( WIN32 )
+ include(win32_target_environment_path)
+ get_target_environment_path(ESCAPED_PATH)
+elseif(CMAKE_BUILD_TYPE)
+ string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_BUILD_TYPE)
+ if (UPPER_BUILD_TYPE STREQUAL "DEBUG")
+ set(EXAMPLE_COMPILE_DEFINITIONS _DEBUG)
+ endif()
+else()
+ set(EXAMPLE_COMPILE_DEFINITIONS _DEBUG)
+endif()
foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES)
add_executable(${exampleName} ${exampleName}.cpp)
- target_compile_definitions(${exampleName} PRIVATE
${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS}
${APR_UTIL_COMPILE_DEFINITIONS} )
+ if(${exampleName} STREQUAL auto-configured)
+ target_sources(${exampleName} PRIVATE UserLib/logmanager.cpp)
+ endif()
+ target_compile_definitions(${exampleName} PRIVATE
${EXAMPLE_COMPILE_DEFINITIONS} ${LOG4CXX_COMPILE_DEFINITIONS}
${APR_COMPILE_DEFINITIONS} ${APR_UTIL_COMPILE_DEFINITIONS} )
target_include_directories(${exampleName} PRIVATE
${CMAKE_CURRENT_LIST_DIR} $<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
target_link_libraries(${exampleName} PRIVATE log4cxx ${APR_UTIL_LIBRARIES}
${EXPAT_LIBRARIES} ${APR_LIBRARIES} ${APR_SYSTEM_LIBS})
+ if( WIN32 )
+ set_target_properties(${exampleName} PROPERTIES
+ VS_DEBUGGER_ENVIRONMENT "PATH=${ESCAPED_PATH}"
+ VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+ )
+ endif()
endforeach()
configure_file( custom-appender.xml
@@ -35,4 +55,9 @@ if(${fmt_FOUND})
target_compile_definitions(format-string PRIVATE
${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS}
${APR_UTIL_COMPILE_DEFINITIONS} )
target_include_directories(format-string PRIVATE ${CMAKE_CURRENT_LIST_DIR}
$<TARGET_PROPERTY:log4cxx,INCLUDE_DIRECTORIES>)
target_link_libraries(format-string PRIVATE log4cxx ${APR_UTIL_LIBRARIES}
${EXPAT_LIBRARIES} ${APR_LIBRARIES} ${APR_SYSTEM_LIBS} fmt::fmt)
+ if( WIN32 )
+ set_target_properties( format-string PROPERTIES
+ VS_DEBUGGER_ENVIRONMENT "PATH=${ESCAPED_PATH}"
+ )
+ endif()
endif(${fmt_FOUND})
diff --git a/src/examples/cpp/UserLib/logmanager.cpp
b/src/examples/cpp/UserLib/logmanager.cpp
new file mode 100644
index 00000000..9c39ffb9
--- /dev/null
+++ b/src/examples/cpp/UserLib/logmanager.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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 <UserLib/logmanager.h>
+#include <log4cxx/logmanager.h>
+#include <log4cxx/logstring.h>
+#include <log4cxx/defaultconfigurator.h>
+#include <log4cxx/helpers/pool.h>
+#include <log4cxx/spi/loggerrepository.h>
+#include <log4cxx/file.h>
+#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/transcoder.h>
+
+#ifdef WIN32
+#include <Windows.h>
+#elif __APPLE__
+#include <mach-o/dyld.h>
+#else
+#include <unistd.h> /* getpid */
+#endif
+
+namespace
+{
+
+using namespace log4cxx;
+
+// Get a list of file base names that may contain configuration data
+// and put an alternate path into \c altPrefix
+ std::vector<std::string>
+DefaultConfigurationFileNames(std::string& altPrefix)
+{
+ std::vector<std::string> result;
+
+ // 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__)
+ _NSGetExecutablePath(buf, &bufCount);
+#else
+ std::ostringstream exeLink;
+ exeLink << "/proc/" << getpid() << "/exe";
+ bufCount = readlink(exeLink.str().c_str(), buf, bufSize);
+ if (0 < bufCount)
+ buf[bufCount] = 0;
+#endif
+ std::string programFileName(buf);
+ auto slashIndex = programFileName.rfind(pathSepar);
+ if (std::string::npos != slashIndex)
+ {
+ // Extract the path
+ altPrefix = programFileName.substr(0, slashIndex + 1);
+#if defined(_DEBUG)
+ LogString msg1 = LOG4CXX_STR("Alternate prefix [");
+ helpers::Transcoder::decode(altPrefix, msg1);
+ msg1 += LOG4CXX_STR("]");
+ helpers::LogLog::debug(msg1);
+#endif
+ // Add a local directory relative name
+ result.push_back(programFileName.substr(slashIndex + 1));
+#if defined(_DEBUG)
+ LogString msg2(LOG4CXX_STR("Alternate configuration file name
["));
+ helpers::Transcoder::decode(result.back(), msg2);
+ msg2 += LOG4CXX_STR("]");
+ helpers::LogLog::debug(msg2);
+#endif
+ // Add a local directory relative name without any extension
+ auto dotIndex = result.back().rfind('.');
+ if (std::string::npos != dotIndex)
+ {
+ result.push_back(result.back());
+ result.back().erase(dotIndex);
+#if defined(_DEBUG)
+ LogString msg3(LOG4CXX_STR("Alternate configuration
file name ["));
+ helpers::Transcoder::decode(result.back(), msg3);
+ msg3 += LOG4CXX_STR("]");
+ helpers::LogLog::debug(msg3);
+#endif
+ }
+ }
+ else if (!programFileName.empty())
+ {
+ auto dotIndex = result.back().rfind('.');
+ if (std::string::npos != dotIndex)
+ {
+ programFileName.erase(dotIndex);
+ result.push_back(programFileName);
+#if defined(_DEBUG)
+ LogString msg(LOG4CXX_STR("Alternate configuration file
name ["));
+ helpers::Transcoder::decode(result.back(), msg);
+ msg += LOG4CXX_STR("]");
+ helpers::LogLog::debug(msg);
+#endif
+ }
+ }
+ result.push_back("log4cxx");
+ result.push_back("log4j");
+ return result;
+}
+
+struct log4cxx_initializer
+{
+ log4cxx_initializer()
+ {
+#if defined(_DEBUG)
+ helpers::LogLog::setInternalDebugging(true);
+#endif
+ const char* extension[] = { ".xml", ".properties", 0 };
+ std::string altPrefix;
+ log4cxx::helpers::Pool pool;
+
+ for (auto baseName : DefaultConfigurationFileNames(altPrefix))
+ {
+ int i = 0;
+ for (; extension[i]; ++i)
+ {
+ log4cxx::File
current_working_dir_candidate(baseName + extension[i]);
+ if (current_working_dir_candidate.exists(pool))
+ {
+
log4cxx::DefaultConfigurator::setConfigurationFileName(current_working_dir_candidate.getPath());
+
log4cxx::DefaultConfigurator::setConfigurationWatchSeconds(5);
+ break;
+ }
+ if (!altPrefix.empty())
+ {
+ log4cxx::File
alt_dir_candidate(altPrefix + baseName + extension[i]);
+ if (alt_dir_candidate.exists(pool))
+ {
+
log4cxx::DefaultConfigurator::setConfigurationFileName(alt_dir_candidate.getPath());
+
log4cxx::DefaultConfigurator::setConfigurationWatchSeconds(5);
+ break;
+ }
+ }
+ }
+ if (extension[i]) // Found a configuration file?
+ break;
+ }
+ }
+ ~log4cxx_initializer()
+ {
+ log4cxx::LogManager::shutdown();
+ }
+};
+
+} // namespace
+
+namespace UserLib
+{
+
+ log4cxx::LoggerPtr
+getLogger(const std::string& name)
+{
+ static log4cxx_initializer initAndShutdown;
+ return name.empty()
+ ? log4cxx::LogManager::getRootLogger()
+ : log4cxx::LogManager::getLogger(name);
+}
+
+} // namespace UserLib
diff --git a/src/examples/cpp/UserLib/logmanager.h
b/src/examples/cpp/UserLib/logmanager.h
new file mode 100644
index 00000000..a23d7adc
--- /dev/null
+++ b/src/examples/cpp/UserLib/logmanager.h
@@ -0,0 +1,8 @@
+#include <log4cxx/logger.h>
+
+namespace UserLib
+{
+
+extern log4cxx::LoggerPtr getLogger(const std::string& name = std::string());
+
+} // namespace UserLib
diff --git a/src/examples/cpp/auto-configured.cpp
b/src/examples/cpp/auto-configured.cpp
new file mode 100644
index 00000000..93333209
--- /dev/null
+++ b/src/examples/cpp/auto-configured.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <UserLib/logmanager.h>
+
+ log4cxx::LoggerPtr
+rootLogger(UserLib::getLogger());
+
+struct ExampleStaticData
+{
+ ExampleStaticData()
+ {
+ LOG4CXX_DEBUG(rootLogger, "static initiallizer message");
+ }
+};
+
+static ExampleStaticData data;
+
+int main()
+{
+ int result = EXIT_SUCCESS;
+ LOG4CXX_INFO(rootLogger, "main function message");
+ return EXIT_SUCCESS;
+}
diff --git a/src/examples/cpp/auto-configured.xml
b/src/examples/cpp/auto-configured.xml
new file mode 100644
index 00000000..77666bbc
--- /dev/null
+++ b/src/examples/cpp/auto-configured.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <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"/>
+ </layout>
+ </appender>
+
+ <root>
+ <priority value="debug" />
+ <appender-ref ref="ConsoleAppender"/>
+ </root>
+
+</log4j:configuration>
diff --git a/src/main/cpp/defaultconfigurator.cpp
b/src/main/cpp/defaultconfigurator.cpp
index db01845d..b7df7239 100644
--- a/src/main/cpp/defaultconfigurator.cpp
+++ b/src/main/cpp/defaultconfigurator.cpp
@@ -23,12 +23,11 @@
#include <log4cxx/helpers/optionconverter.h>
#include <log4cxx/helpers/stringhelper.h>
-
using namespace log4cxx;
using namespace log4cxx::spi;
using namespace log4cxx::helpers;
-namespace
+namespace
{
LogString DefaultConfiguratorPath;
int DefaultConfiguratorWatchSeconds = 0;
@@ -112,7 +111,6 @@ void DefaultConfigurator::configure(LoggerRepositoryPtr
repository)
}
-
const LogString DefaultConfigurator::getConfiguratorClass()
{
diff --git a/src/main/include/log4cxx/defaultconfigurator.h
b/src/main/include/log4cxx/defaultconfigurator.h
index 9c296b62..0825f2d7 100644
--- a/src/main/include/log4cxx/defaultconfigurator.h
+++ b/src/main/include/log4cxx/defaultconfigurator.h
@@ -38,14 +38,22 @@ 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 variables "LOG4CXX_CONFIGURATION" and
"log4j.configuration" are examined.
+ Unless a custom configurator is specified using the
+ "LOG4CXX_CONFIGURATOR_CLASS" or "log4j.configuratorClass"
+ environment variable, the PropertyConfigurator will be used to
+ configure log4cxx unless the file name ends with the ".xml"
+ extension, in which case the DOMConfigurator will be used. If a
+ custom configurator is specified, the environment variable
should
+ contain a fully qualified class name of a class that implements
the
+ Configurator interface.
- If the configuration file name has not been provided by any of
the previous approaches
- the current directory is examined for a file named either
- "log4cxx.xml", "log4cxx.properties", "log4j.xml" or
"log4j.properties".
+ If the configuration file name is not found using any of the
previous approaches,
+ the current directory is examined for a file with extension
".xml" or ".properties"
+ with a base name "log4cxx" or "log4j".
If a positive number has been provided by a call to
setConfigurationWatchSeconds()
- or the environment variables
'LOG4CXX_CONFIGURATION_WATCH_SECONDS' contains a positive number
+ 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.
.*/
diff --git a/src/main/include/log4cxx/hierarchy.h
b/src/main/include/log4cxx/hierarchy.h
index 1f3c55a4..058dfdfe 100644
--- a/src/main/include/log4cxx/hierarchy.h
+++ b/src/main/include/log4cxx/hierarchy.h
@@ -79,7 +79,7 @@ class LOG4CXX_EXPORT Hierarchy :
void addHierarchyEventListener(const
spi::HierarchyEventListenerPtr& listener) override;
/**
- * Load the configuration if not yet configured.
+ * Call DefaultConfigurator::configure if not yet configured.
*/
void autoConfigure() override;
@@ -128,10 +128,10 @@ class LOG4CXX_EXPORT Hierarchy :
LevelPtr getThreshold() const override;
/**
- Return a new logger instance named as the first parameter using
- the default factory.
+ Retrieve the \c name Logger instance using
+ the default factory to create it if required.
- <p>If a logger of that name already exists, then it will be
+ If a logger of that name already exists, then it will be
returned. Otherwise, a new logger will be instantiated and
then linked with its existing ancestors as well as children.
@@ -141,10 +141,10 @@ class LOG4CXX_EXPORT Hierarchy :
LoggerPtr getLogger(const LogString& name) override;
/**
- Return a new logger instance named as the first parameter using
- <code>factory</code>.
+ Retrieve the \c name Logger instance using
+ <code>factory</code> to create it if required.
- <p>If a logger of that name already exists, then it will be
+ If a logger of that name already exists, then it will be
returned. Otherwise, a new logger will be instantiated by the
<code>factory</code> parameter and linked with its existing
ancestors as well as children.
diff --git a/src/main/include/log4cxx/logmanager.h
b/src/main/include/log4cxx/logmanager.h
index d347f9aa..109ff98c 100644
--- a/src/main/include/log4cxx/logmanager.h
+++ b/src/main/include/log4cxx/logmanager.h
@@ -54,20 +54,20 @@ class LOG4CXX_EXPORT LogManager
public:
/**
- Sets <code>LoggerFactory</code> but only if the correct
- <em>guard</em> is passed as parameter.
+ Use \c selector to source the {@link spi::LoggerRepository
LoggerRepository}, but only if the correct
+ \c guard is passed as parameter.
<p>Initally the guard is null. If the guard is
<code>null</code>, then invoking this method sets the logger
factory and the guard. Following invocations will throw a {@link
helpers::IllegalArgumentException IllegalArgumentException},
- unless the previously set <code>guard</code> is passed
as the second
- parameter.
+ unless the previously set \c guard is passed as the second
+ parameter.
<p>This allows a high-level component to set the {@link
spi::RepositorySelector RepositorySelector} used by the
- <code>LogManager</code>.
- */
+ LogManager.
+ */
static void setRepositorySelector(spi::RepositorySelectorPtr
selector,
void* guard);
@@ -75,117 +75,136 @@ class LOG4CXX_EXPORT LogManager
static spi::LoggerRepositoryPtr getLoggerRepository();
/**
- Retrieve the appropriate root logger.
+ Retrieve the root logger from the {@link spi::LoggerRepository
LoggerRepository}.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getRootLogger();
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name in current encoding.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using DefaultLoggerFactory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const std::string& name);
+
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name in current encoding.
- * @param factory logger factory.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using \c factory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const std::string& name,
const spi::LoggerFactoryPtr& factory);
/**
- * Determines if logger name exists in the hierarchy.
- * @param name logger name.
- * @return true if logger exists.
+ Does the logger \c name exist in the hierarchy?
*/
static LoggerPtr exists(const std::string& name);
#if LOG4CXX_WCHAR_T_API
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using DefaultLoggerFactory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const std::wstring& name);
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @param factory logger factory.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using \c factory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const std::wstring& name,
const spi::LoggerFactoryPtr& factory);
/**
- * Determines if logger name exists in the hierarchy.
- * @param name logger name.
- * @return true if logger exists.
+ Does the logger \c name exist in the hierarchy?
*/
static LoggerPtr exists(const std::wstring& name);
#endif
#if LOG4CXX_UNICHAR_API
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using DefaultLoggerFactory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const std::basic_string<UniChar>&
name);
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @param factory logger factory.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using \c factory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const std::basic_string<UniChar>&
name,
const spi::LoggerFactoryPtr& factory);
/**
- * Determines if logger name exists in the hierarchy.
- * @param name logger name.
- * @return true if logger exists.
+ Does the logger \c name exist in the hierarchy?
*/
static LoggerPtr exists(const std::basic_string<UniChar>& name);
#endif
#if LOG4CXX_CFSTRING_API
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using DefaultLoggerFactory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const CFStringRef& name);
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @param factory logger factory.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using \c factory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLogger(const CFStringRef& name,
const spi::LoggerFactoryPtr& factory);
/**
- * Determines if logger name exists in the hierarchy.
- * @param name logger name.
- * @return true if logger exists.
+ Does the logger \c name exist in the hierarchy?
*/
static LoggerPtr exists(const CFStringRef& name);
#endif
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using DefaultLoggerFactory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLoggerLS(const LogString& name);
/**
- Retrieve the appropriate Logger instance.
- * @param name logger name.
- * @param factory logger factory.
- * @return logger.
+ Retrieve the \c name Logger instance from the
+ {@link spi::LoggerRepository LoggerRepository}
+ using \c factory to create it if required.
+
+ Call {@link spi::LoggerRepository::autoConfigure autoConfigure}
+ if the repository is not yet configured.
*/
static LoggerPtr getLoggerLS(const LogString& name,
const spi::LoggerFactoryPtr& factory);
/**
- * Determines if logger name exists in the hierarchy.
- * @param name logger name.
- * @return true if logger exists.
+ Does the logger \c name exist in the hierarchy?
*/
static LoggerPtr existsLS(const LogString& name);
diff --git a/src/site/markdown/usage.md b/src/site/markdown/usage.md
index 232aa7c9..aefa0df1 100644
--- a/src/site/markdown/usage.md
+++ b/src/site/markdown/usage.md
@@ -301,7 +301,11 @@ additivity flag set to *false*, then *C*'s output will be
directed to
all the appenders in *C* and it's ancestors up to and including *P* but,
not the appenders in any of the ancestors of *P*.
-Loggers have their additivity flag set to *true* by default.
+Loggers have their additivity flag set to *true* by default,
+meaning output goes to the appender attached to a
+parent [Logger](@ref log4cxx.Logger).
+It is therefore often sufficient to configure or attach an appender
+only to the root logger in the [Hierarchy](@ref log4cxx.Hierarchy).
The table below shows an
example:
@@ -595,27 +599,21 @@ event to a second log4cxx server.
# Default Initialization Procedure {#default-initialization-procedure}
The log4cxx library does not make any assumptions about its environment.
-In particular, there are no default log4cxx appenders. Under certain
-well-defined circumstances however, the static inializer of the *Logger*
-class will attempt to automatically configure log4cxx.
-
-The exact default initialization algorithm is defined as follows:
-
-1. Set the configurationOptionStr string variable to the value of the
- **LOG4CXX\_CONFIGURATION** environment variable if set, otherwise
- the value of the **log4j.configuration** environment variable if
- set, otherwise the first of the following file names which exist in
- the current working directory, "log4cxx.xml", "log4cxx.properties",
- "log4j.xml" and "log4j.properties". If configurationOptionStr has
- not been set, then disable logging.
-2. Unless a custom configurator is specified using the
- **LOG4CXX\_CONFIGURATOR\_CLASS** or **log4j.configuratorClass**
- environment variable, the PropertyConfigurator will be used to
- configure log4cxx unless the file name ends with the ".xml"
- extension, in which case the DOMConfigurator will be used. If a
- custom configurator is specified, the environment variable should
- contain a fully qualified class name of a class that implements the
- Configurator interface.
+In particular, when initially created the root [Logger](@ref log4cxx.Logger)
has no appender.
+However the library will attempt automatic configuration.
+
+If the LoggerRepositoy is not yet configured on the first call to
+[getLogger](@ref log4cxx.LogManager.getLogger) of [LogManager](@ref
log4cxx.LogManager),
+the [autoConfigure](@ref log4cxx.spi.LoggerRepository.autoConfigure) virtual
method
+of [LoggerRepository](@ref log4cxx.spi.LoggerRepository) is called.
+
+The [default autoConfigure](@ref log4cxx.Hierarchy.autoConfigure)
implementation
+calls the [configure](@ref log4cxx.DefaultConfigurator.configure) method
+of the [DefaultConfigurator](@ref log4cxx.DefaultConfigurator) class.
+
+To use automatic configuration with a non-standard file name
+create and use your own wrapper for [getLogger](@ref
log4cxx.LogManager.getLogger).
+A full example can be seen in the src/examples/cpp/UserLib/logmanager.cpp file.
# Nested Diagnostic Contexts {#nested-diagnostic-contexts}
@@ -697,20 +695,27 @@ The user should be aware of the following performance
issues.
1. **Logging performance when logging is turned off.**
- When logging is turned off entirely or just for a set of levels, the
- cost of a log request consists of a method invocation plus an
- integer comparison. The LOG4CXX\_DEBUG and similar macros suppress
- unnecessary expression evaluation if the request is not enabled.
+ The LOG4CXX\_DEBUG and similar macros have a
+ cost of an in-lined null pointer check plus an integer comparison
+ when the logger not currently enabled for that level.
+ The other terms inside the macro are not evaluated.
+
+ When the level is enabled for a logger but the logging hierarchy is turned
off
+ entirely or just for a set of levels, the cost of a log request consists
+ of a method invocation plus an integer comparison.
-2. **The performance of deciding whether to log or not to log when
- logging is turned on.**
+2. **Actually outputting log messages**
+
+ This is the cost of formatting the log output and sending it to its
+ target destination. Here again, a serious effort was made to make
+ layouts (formatters) perform as quickly as possible. The same is
+ true for appenders.
+
+3. **The cost of changing a logger's level.**
- This is essentially the performance of walking the logger hierarchy.
- When logging is turned on, log4cxx still needs to compare the level
- of the log request with the level of the request logger. However,
- loggers may not have an assigned level; they can inherit them from
- the logger hierarchy. Thus, before inheriting a level, the logger
- may need to search its ancestors.
+ The threshold value stored in any child logger is updated.
+ This is done iterating over the map of all known logger objects
+ and walking the hierarchy of each.
There has been a serious effort to make this hierarchy walk to be as
fast as possible. For example, child loggers link only to their
@@ -719,16 +724,6 @@ The user should be aware of the following performance
issues.
root logger, thereby circumventing the nonexistent *com* or
*com.foo* loggers. This significantly improves the speed of the
walk, especially in "sparse" hierarchies.
-
- The cost of walking the hierarchy is typically 3 times slower than
- when logging is turned off entirely.
-
-3. **Actually outputting log messages**
-
- This is the cost of formatting the log output and sending it to its
- target destination. Here again, a serious effort was made to make
- layouts (formatters) perform as quickly as possible. The same is
- true for appenders.
# Removing log statements {#removing-log-statements}
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 0189b583..0e9cad93 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -82,29 +82,9 @@ add_subdirectory(xml)
add_subdirectory(throughput)
add_subdirectory(benchmark)
-# Note: we need to include the APR DLLs on our path so that the tests will run.
-# The way that CMake sets the environment is that it actually generates a
secondary file,
-# CTestTestfile.cmake, which sets the final properties of the test.
-# However, this results in a secondary quirk to the running of the tests:
CMake uses
-# a semicolon to deliminate entries in a list! Since the Windows PATH is
semicolon-delimited
-# as well, CMake uses only the first entry in the list when setting the path.
-# So, we need to do a triple escape on the PATH that we want to set in order
for CMake to
-# properly interpret the PATH
if( WIN32 )
- get_filename_component(APR_DLL_DIR "${APR_DLL}" DIRECTORY)
- get_filename_component(APR_UTIL_DLL_DIR "${APR_UTIL_DLL}" DIRECTORY)
- get_filename_component(EXPAT_LIB_DIR "${EXPAT_LIBRARY}" DIRECTORY)
-
-
- set(EXPAT_DLL_DIR "${EXPAT_LIB_DIR}/../bin")
- set(LOG4CXX_DLL_DIR "$<SHELL_PATH:$<TARGET_FILE_DIR:log4cxx>>;")
- set(PATH_FOR_TESTS
${CMAKE_PROGRAM_PATH};${APR_DLL_DIR};${APR_UTIL_DLL_DIR};${LOG4CXX_DLL_DIR};${EXPAT_DLL_DIR}\;)
- list(REMOVE_DUPLICATES PATH_FOR_TESTS)
- set(NORMAL_PATH $ENV{PATH})
- set(ESCAPED_PATH "")
- foreach( ENTRY ${PATH_FOR_TESTS}${NORMAL_PATH} )
- set(ESCAPED_PATH "${ESCAPED_PATH}${ENTRY}\\\;")
- endforeach()
+ include(win32_target_environment_path)
+ get_target_environment_path(ESCAPED_PATH)
elseif(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_BUILD_TYPE)
if (UPPER_BUILD_TYPE STREQUAL "DEBUG")
diff --git a/src/test/cpp/autoconfiguretestcase.cpp
b/src/test/cpp/autoconfiguretestcase.cpp
index 629ef630..e486727e 100644
--- a/src/test/cpp/autoconfiguretestcase.cpp
+++ b/src/test/cpp/autoconfiguretestcase.cpp
@@ -70,7 +70,7 @@ LOGUNIT_CLASS(AutoConfigureTestCase)
#endif
helpers::Pool m_pool;
char m_buf[2048];
- LogString m_configFile = LOG4CXX_STR("autoConfigureTest.properties");
+ LogString m_configFile =
LOG4CXX_STR("autoconfiguretestcase.properties");
public:
void copyPropertyFile()
@@ -78,7 +78,7 @@ public:
LOGUNIT_ASSERT(File(LOG4CXX_STR("input/autoConfigureTest.properties")).exists(m_pool));
LOGUNIT_ASSERT(apr_file_copy
( "input/autoConfigureTest.properties"
- , "autoConfigureTest.properties"
+ , "autoconfiguretestcase.properties"
, APR_FPROT_UREAD | APR_FPROT_UWRITE
, m_pool.getAPRPool()
) == APR_SUCCESS);
@@ -91,7 +91,7 @@ public:
void shutdown()
{
LogManager::shutdown();
- LOGUNIT_ASSERT(apr_file_remove("autoConfigureTest.properties",
m_pool.getAPRPool()) == APR_SUCCESS);
+
LOGUNIT_ASSERT(apr_file_remove("autoconfiguretestcase.properties",
m_pool.getAPRPool()) == APR_SUCCESS);
}
void test1()