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 f334447c LOGCXX-560 logging in a static initializer (#159)
f334447c is described below
commit f334447cd472336f7adbb1313003b38d1b8d6ecf
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Dec 17 12:41:34 2022 +1100
LOGCXX-560 logging in a static initializer (#159)
* Add a simple example
* Change existing example to one in which logging in a static initializer
works.
* Use \include for example code
---
src/examples/cpp/CMakeLists.txt | 5 +-
src/examples/cpp/MyApp.properties | 17 ++
src/examples/cpp/MyApp1.cpp | 24 ++
src/examples/cpp/MyApp2.cpp | 17 ++
src/examples/cpp/UserLib/logmanager.cpp | 97 ++++----
src/examples/cpp/UserLib/logmanager.h | 3 +-
src/examples/cpp/auto-configured.cpp | 21 +-
src/examples/cpp/com/foo/bar.cpp | 9 +
src/examples/cpp/com/foo/bar.h | 13 +
src/examples/cpp/com/foo/config.h | 10 +
src/examples/cpp/com/foo/config1.cpp | 22 ++
src/examples/cpp/com/foo/config2.cpp | 21 ++
src/main/include/log4cxx/patternlayout.h | 4 +-
src/site/doxy/Doxyfile.in | 2 +-
src/site/markdown/usage.md | 415 +++++++++++++------------------
15 files changed, 365 insertions(+), 315 deletions(-)
diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index 2cce6747..0f6d0dab 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -15,7 +15,7 @@
# limitations under the License.
#
-set(ALL_LOG4CXX_EXAMPLES auto-configured console delayedloop stream trivial
custom-appender)
+set(ALL_LOG4CXX_EXAMPLES auto-configured console delayedloop stream trivial
custom-appender MyApp1 MyApp2)
if( WIN32 )
include(win32_target_environment_path)
get_target_environment_path(ESCAPED_PATH)
@@ -33,6 +33,9 @@ foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES)
if(${exampleName} STREQUAL auto-configured)
target_sources(${exampleName} PRIVATE UserLib/logmanager.cpp)
endif()
+ if(${exampleName} STREQUAL MyApp2)
+ target_sources(${exampleName} PRIVATE com/foo/config2.cpp
com/foo/bar.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})
diff --git a/src/examples/cpp/MyApp.properties
b/src/examples/cpp/MyApp.properties
new file mode 100644
index 00000000..386d5051
--- /dev/null
+++ b/src/examples/cpp/MyApp.properties
@@ -0,0 +1,17 @@
+log4j.rootLogger=debug, stdout, R
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+# Pattern to output the caller's file name and line number.
+log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%f:%L) - %m%n
+
+log4j.appender.R=org.apache.log4j.RollingFileAppender
+log4j.appender.R.File=example.log
+
+log4j.appender.R.MaxFileSize=100KB
+# Keep one backup file
+log4j.appender.R.MaxBackupIndex=1
+
+log4j.appender.R.layout=org.apache.log4j.PatternLayout
+log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
diff --git a/src/examples/cpp/MyApp1.cpp b/src/examples/cpp/MyApp1.cpp
new file mode 100644
index 00000000..2cc170bd
--- /dev/null
+++ b/src/examples/cpp/MyApp1.cpp
@@ -0,0 +1,24 @@
+#include <log4cxx/logger.h>
+#include <log4cxx/basicconfigurator.h>
+
+static auto logger = log4cxx::Logger::getLogger("MyApp");
+
+void foo() {
+ // Get a logger that is a child of the statically declared logger
+ auto fooLogger = log4cxx::Logger::getLogger("MyApp.foo");
+ LOG4CXX_TRACE(fooLogger, "Doing foo at trace level");
+ LOG4CXX_DEBUG(fooLogger, "Doing foo at debug level");
+ LOG4CXX_INFO(fooLogger, "Doing foo at info level");
+ LOG4CXX_WARN(fooLogger, "Doing foo at warn level");
+ LOG4CXX_ERROR(fooLogger, "Doing foo at error level");
+ LOG4CXX_FATAL(fooLogger, "Doing foo at fatal level");
+}
+
+int main(int argc, char **argv) {
+ // Log to standard output.
+ log4cxx::BasicConfigurator::configure();
+ LOG4CXX_INFO(logger, "Entering application.");
+ foo();
+ LOG4CXX_INFO(logger, "Exiting application.");
+ return EXIT_SUCCESS;
+}
diff --git a/src/examples/cpp/MyApp2.cpp b/src/examples/cpp/MyApp2.cpp
new file mode 100644
index 00000000..0c7baccc
--- /dev/null
+++ b/src/examples/cpp/MyApp2.cpp
@@ -0,0 +1,17 @@
+#include "com/foo/config.h"
+#include "com/foo/bar.h"
+
+int main(int argc, char **argv) {
+ int result = EXIT_SUCCESS;
+ try {
+ auto logger = com::foo::getLogger("MyApp");
+ LOG4CXX_INFO(logger, "Entering application.");
+ com::foo::Bar bar;
+ bar.doIt();
+ LOG4CXX_INFO(logger, "Exiting application.");
+ }
+ catch(std::exception&) {
+ result = EXIT_FAILURE;
+ }
+ return result;
+}
diff --git a/src/examples/cpp/UserLib/logmanager.cpp
b/src/examples/cpp/UserLib/logmanager.cpp
index 9c39ffb9..d56cd4ea 100644
--- a/src/examples/cpp/UserLib/logmanager.cpp
+++ b/src/examples/cpp/UserLib/logmanager.cpp
@@ -32,16 +32,14 @@
#include <unistd.h> /* getpid */
#endif
-namespace
-{
+// Local functions
+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)
-{
+auto DefaultConfigurationFileNames(std::string& altPrefix) ->
std::vector<std::string> {
std::vector<std::string> result;
// Find the executable file name
@@ -62,8 +60,7 @@ DefaultConfigurationFileNames(std::string& altPrefix)
#endif
std::string programFileName(buf);
auto slashIndex = programFileName.rfind(pathSepar);
- if (std::string::npos != slashIndex)
- {
+ if (std::string::npos != slashIndex) {
// Extract the path
altPrefix = programFileName.substr(0, slashIndex + 1);
#if defined(_DEBUG)
@@ -82,8 +79,7 @@ DefaultConfigurationFileNames(std::string& altPrefix)
#endif
// Add a local directory relative name without any extension
auto dotIndex = result.back().rfind('.');
- if (std::string::npos != dotIndex)
- {
+ if (std::string::npos != dotIndex) {
result.push_back(result.back());
result.back().erase(dotIndex);
#if defined(_DEBUG)
@@ -94,11 +90,9 @@ DefaultConfigurationFileNames(std::string& altPrefix)
#endif
}
}
- else if (!programFileName.empty())
- {
+ else if (!programFileName.empty()) {
auto dotIndex = result.back().rfind('.');
- if (std::string::npos != dotIndex)
- {
+ if (std::string::npos != dotIndex) {
programFileName.erase(dotIndex);
result.push_back(programFileName);
#if defined(_DEBUG)
@@ -114,59 +108,54 @@ DefaultConfigurationFileNames(std::string& altPrefix)
return result;
}
-struct log4cxx_initializer
-{
- log4cxx_initializer()
- {
+// Provide the name of the configuration file to [DefaultConfigurator](@ref
log4cxx.DefaultConfigurator).
+// Set up a background thread that will check for changes every 5 seconds and
reload the configuration
+void SelectConfigurationFile() {
#if defined(_DEBUG)
- helpers::LogLog::setInternalDebugging(true);
+ helpers::LogLog::setInternalDebugging(true);
#endif
- const char* extension[] = { ".xml", ".properties", 0 };
- std::string altPrefix;
- log4cxx::helpers::Pool pool;
+ const char* extension[] = { ".xml", ".properties", 0 };
+ std::string altPrefix;
+ 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);
+ for (auto baseName : DefaultConfigurationFileNames(altPrefix)) {
+ int i = 0;
+ for (; extension[i]; ++i) {
+ File current_working_dir_candidate(baseName +
extension[i]);
+ if (current_working_dir_candidate.exists(pool)) {
+
DefaultConfigurator::setConfigurationFileName(current_working_dir_candidate.getPath());
+
DefaultConfigurator::setConfigurationWatchSeconds(5);
+ break;
+ }
+ if (!altPrefix.empty()) {
+ File alt_dir_candidate(altPrefix + baseName +
extension[i]);
+ if (alt_dir_candidate.exists(pool)) {
+
DefaultConfigurator::setConfigurationFileName(alt_dir_candidate.getPath());
+
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;
}
+ if (extension[i]) // Found a configuration file?
+ break;
}
- ~log4cxx_initializer()
- {
- log4cxx::LogManager::shutdown();
- }
-};
+}
} // namespace
-namespace UserLib
-{
+namespace UserLib {
- log4cxx::LoggerPtr
-getLogger(const std::string& name)
-{
- static log4cxx_initializer initAndShutdown;
+// Retrieve the \c name logger.
+// Configure Log4cxx on the first call.
+auto getLogger(const std::string& name) -> log4cxx::LoggerPtr {
+ static struct log4cxx_initializer {
+ log4cxx_initializer() {
+ SelectConfigurationFile();
+ }
+ ~log4cxx_initializer() {
+ log4cxx::LogManager::shutdown();
+ }
+ } initialiser;
return name.empty()
? log4cxx::LogManager::getRootLogger()
: log4cxx::LogManager::getLogger(name);
diff --git a/src/examples/cpp/UserLib/logmanager.h
b/src/examples/cpp/UserLib/logmanager.h
index a23d7adc..31274093 100644
--- a/src/examples/cpp/UserLib/logmanager.h
+++ b/src/examples/cpp/UserLib/logmanager.h
@@ -3,6 +3,7 @@
namespace UserLib
{
-extern log4cxx::LoggerPtr getLogger(const std::string& name = std::string());
+ extern auto
+getLogger(const std::string& name = std::string()) -> log4cxx::LoggerPtr;
} // namespace UserLib
diff --git a/src/examples/cpp/auto-configured.cpp
b/src/examples/cpp/auto-configured.cpp
index 93333209..b5c42e3b 100644
--- a/src/examples/cpp/auto-configured.cpp
+++ b/src/examples/cpp/auto-configured.cpp
@@ -14,24 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <UserLib/logmanager.h>
+#include "UserLib/logmanager.h"
- log4cxx::LoggerPtr
-rootLogger(UserLib::getLogger());
+extern auto rootLogger = UserLib::getLogger();
-struct ExampleStaticData
-{
- ExampleStaticData()
- {
- LOG4CXX_DEBUG(rootLogger, "static initiallizer message");
+static struct ExampleStaticData {
+ ExampleStaticData() {
+ LOG4CXX_DEBUG(rootLogger, "static initializer message");
}
-};
+} static_object;
-static ExampleStaticData data;
-
-int main()
-{
- int result = EXIT_SUCCESS;
+int main() {
LOG4CXX_INFO(rootLogger, "main function message");
return EXIT_SUCCESS;
}
diff --git a/src/examples/cpp/com/foo/bar.cpp b/src/examples/cpp/com/foo/bar.cpp
new file mode 100644
index 00000000..e5575d1e
--- /dev/null
+++ b/src/examples/cpp/com/foo/bar.cpp
@@ -0,0 +1,9 @@
+#include "com/foo/bar.h"
+
+using namespace com::foo;
+
+LoggerPtr Bar::m_logger(getLogger("com.foo.bar"));
+
+void Bar::doIt() {
+ LOG4CXX_DEBUG(m_logger, "Did it again!");
+}
diff --git a/src/examples/cpp/com/foo/bar.h b/src/examples/cpp/com/foo/bar.h
new file mode 100644
index 00000000..9ebfb489
--- /dev/null
+++ b/src/examples/cpp/com/foo/bar.h
@@ -0,0 +1,13 @@
+#ifndef COM_FOO_BAR_H_
+#define COM_FOO_BAR_H_
+#include "com/foo/config.h"
+namespace com { namespace foo {
+
+class Bar {
+ static LoggerPtr m_logger;
+ public:
+ void doIt();
+};
+
+} } // namespace com::foo
+#endif // COM_FOO_BAR_H_
diff --git a/src/examples/cpp/com/foo/config.h
b/src/examples/cpp/com/foo/config.h
new file mode 100644
index 00000000..02155999
--- /dev/null
+++ b/src/examples/cpp/com/foo/config.h
@@ -0,0 +1,10 @@
+#ifndef COM_FOO_CONFIG_H_
+#define COM_FOO_CONFIG_H_
+#include <log4cxx/logger.h>
+namespace com { namespace foo {
+
+using LoggerPtr = log4cxx::LoggerPtr;
+extern auto getLogger(const std::string& name = std::string()) -> LoggerPtr;
+
+} } // namespace com::foo
+#endif // COM_FOO_CONFIG_H_
diff --git a/src/examples/cpp/com/foo/config1.cpp
b/src/examples/cpp/com/foo/config1.cpp
new file mode 100644
index 00000000..41f0b604
--- /dev/null
+++ b/src/examples/cpp/com/foo/config1.cpp
@@ -0,0 +1,22 @@
+#include "com/foo/config.h"
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/logmanager.h>
+
+namespace com { namespace foo {
+
+auto getLogger(const std::string& name) -> LoggerPtr {
+ static struct log4cxx_initializer {
+ log4cxx_initializer() {
+ // Set up a simple configuration that logs on the
console.
+ log4cxx::BasicConfigurator::configure();
+ }
+ ~log4cxx_initializer() {
+ log4cxx::LogManager::shutdown();
+ }
+ } initAndShutdown;
+ return name.empty()
+ ? log4cxx::LogManager::getRootLogger()
+ : log4cxx::LogManager::getLogger(name);
+}
+
+} } // namespace com::foo
diff --git a/src/examples/cpp/com/foo/config2.cpp
b/src/examples/cpp/com/foo/config2.cpp
new file mode 100644
index 00000000..2c25f8af
--- /dev/null
+++ b/src/examples/cpp/com/foo/config2.cpp
@@ -0,0 +1,21 @@
+#include "com/foo/config.h"
+#include <log4cxx/propertyconfigurator.h>
+#include <log4cxx/logmanager.h>
+
+namespace com { namespace foo {
+
+auto getLogger(const std::string& name) -> LoggerPtr {
+ static struct log4cxx_initializer {
+ log4cxx_initializer() {
+
log4cxx::PropertyConfigurator::configure("MyApp.properties");
+ }
+ ~log4cxx_initializer() {
+ log4cxx::LogManager::shutdown();
+ }
+ } initAndShutdown;
+ return name.empty()
+ ? log4cxx::LogManager::getRootLogger()
+ : log4cxx::LogManager::getLogger(name);
+}
+
+} } // namespace com::foo
diff --git a/src/main/include/log4cxx/patternlayout.h
b/src/main/include/log4cxx/patternlayout.h
index 3aa1f748..0a555e92 100644
--- a/src/main/include/log4cxx/patternlayout.h
+++ b/src/main/include/log4cxx/patternlayout.h
@@ -340,10 +340,10 @@ LOG4CXX_LIST_DEF(FormattingInfoList,
log4cxx::pattern::FormattingInfoPtr);
*
* <p>Below are some examples of conversion patterns.</p>
*
- * <p><strong>%r [%t] %-5p %c %x - %m\n</strong></p>
+ * <p><strong>%%r [%%t] %-5p %%c %%x - %%m\n</strong></p>
* <p>This is essentially the TTCC layout.</p>
*
- * <p><strong>%-6r [%15.15t] %-5p %30.30c %x - %m\n</strong></p>
+ * <p><strong>%-6r [%15.15t] %-5p %30.30c %%x - %%m\n</strong></p>
*
* <p>
* Similar to the TTCC layout except that the relative time is right padded
if less than 6
diff --git a/src/site/doxy/Doxyfile.in b/src/site/doxy/Doxyfile.in
index 8d3ccd2b..a383fa71 100644
--- a/src/site/doxy/Doxyfile.in
+++ b/src/site/doxy/Doxyfile.in
@@ -923,7 +923,7 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include
# command).
-EXAMPLE_PATH =
+EXAMPLE_PATH = src/examples/cpp
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
diff --git a/src/site/markdown/usage.md b/src/site/markdown/usage.md
index 42c8da71..2e9053b8 100644
--- a/src/site/markdown/usage.md
+++ b/src/site/markdown/usage.md
@@ -27,7 +27,7 @@ Usage {#usage}
Log4cxx has three main components: *loggers*, *appenders* and *layouts*.
These three types of components work together to enable developers to
log messages according to message type and level, and to control at
-runtime how these messages are formatted and where they are reported.
+runtime how these messages are formatted and where they are reported.
## Hierarchy {#hierarchy}
@@ -35,34 +35,34 @@ The first and foremost advantage of any logging API over
plain
`std::cout` resides in its ability to disable certain log statements
while allowing others to print unhindered. This capability assumes that
the logging space, that is, the space of all possible logging
-statements, is categorized according to some developer-chosen criteria.
+statements, is categorized according to some developer-chosen criteria.
Loggers are named entities. Logger names are case-sensitive and they
-follow the hierarchical naming rule:
+follow the hierarchical naming rule:
-**Named Hierarchy**
+## Named Hierarchy {#named-hierarchy}
A logger is said to be an *ancestor* of another logger if its name
followed by a dot is a prefix of the *descendant* logger name. A logger
is said to be a *parent* of a *child* logger if there are no ancestors
-between itself and the descendant logger.
+between itself and the descendant logger.
For example, the logger named `com.foo` is a parent of the logger
named `com.foo.Bar`. Similarly, `java` is a parent of `java.util`
and an ancestor of `java.util.Vector`. This naming scheme should be
-familiar to most developers.
+familiar to most developers.
The root logger resides at the top of the logger hierarchy. It is
-exceptional in two ways:
+exceptional in two ways:
-1. it always exists,
-2. it cannot be retrieved by name.
+1. it always exists,
+2. it cannot be retrieved by name.
Invoking the class static
log4cxx::Logger::getRootLogger method retrieves it. All other loggers are
instantiated and retrieved
with the class static log4cxx::Logger::getLogger
method. This method takes the name of the desired logger as a parameter.
-Some of the basic methods in the Logger class are listed below.
+Some of the basic methods in the Logger class are listed below.
~~~{.cpp}
namespace log4cxx {
@@ -74,7 +74,7 @@ Some of the basic methods in the Logger class are listed
below.
static LoggerPtr getLogger(const std::wstring& name);
}
}
-
+
//
// Use these macros instead of calling Logger methods directly.
// Macros will handle char or wchar_t pointers or strings
@@ -93,22 +93,22 @@ Some of the basic methods in the Logger class are listed
below.
Loggers *may* be assigned levels. The pre-defined levels: TRACE, DEBUG,
INFO, WARN, ERROR and FATAL are defined in the
-log4cxx::Level class which provides accessor functions.
+log4cxx::Level class which provides accessor functions.
If a given logger is not assigned a level, then it inherits one from its
-closest ancestor with an assigned level. More formally:
+closest ancestor with an assigned level. More formally:
-**Level Inheritance**
+## Level Inheritance {#level-inheritance}
The *inherited level* for a given logger *C*, is equal to the first
non-null level in the logger hierarchy, starting at *C* and proceeding
-upwards in the hierarchy towards the *root* logger.
+upwards in the hierarchy towards the *root* logger.
To ensure that all loggers can eventually inherit a level, the root
-logger always has an assigned level.
+logger always has an assigned level.
Below are four tables with various assigned level values and the
-resulting inherited levels according to the above rule.
+resulting inherited levels according to the above rule.
| Logger name | Assigned level | Inherited level |
| ----------- | -------------- | --------------- |
@@ -121,7 +121,7 @@ Example 1
In example 1 above, only the root logger is assigned a level. This level
value, *Proot*, is inherited by the other loggers *X*, *X.Y* and
-*X.Y.Z*.
+*X.Y.Z*.
| Logger name | Assigned level | Inherited level |
| ----------- | -------------- | --------------- |
@@ -133,7 +133,7 @@ value, *Proot*, is inherited by the other loggers *X*,
*X.Y* and
Example 2
In example 2, all loggers have an assigned level value. There is no need
-for level inheritence.
+for level inheritence.
| Logger name | Assigned level | Inherited level |
| ----------- | -------------- | --------------- |
@@ -146,7 +146,7 @@ Example 3
In example 3, the loggers *root*, *X* and *X.Y.Z* are assigned the
levels *Proot*, *Px* and *Pxyz* respectively. The logger *X.Y* inherits
-its level value from its parent *X*.
+its level value from its parent *X*.
| Logger name | Assigned level | Inherited level |
| ----------- | -------------- | --------------- |
@@ -160,14 +160,14 @@ Example 4
In example 4, the loggers *root* and *X* and are assigned the levels
*Proot* and *Px* respectively. The loggers *X.Y* and *X.Y.Z* inherits
their level value from their nearest parent *X* having an assigned
-level.
+level.
## Requests {#requests}
Logging requests are made by invoking a method of a logger instance,
preferrably through the use of LOG4CXX\_INFO or similar macros which
support short-circuiting if the threshold is not satisfied and use of
-the insertion operator (\<\<) in the message parameter.
+the insertion operator (\<\<) in the message parameter.
~~~{.cpp}
log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("com.foo"));
@@ -185,68 +185,68 @@ the insertion operator (\<\<) in the message parameter.
A logging request is said to be *enabled* if its level is higher than or
equal to the level of its logger. Otherwise, the request is said to be
*disabled*. A logger without an assigned level will inherit one from the
-hierarchy. This rule is summarized below.
+hierarchy. This rule is summarized below.
-**Basic Selection Rule**
+## Basic Selection Rule {#selection-rule}
A log request of level *p* in a logger with (either assigned or
inherited, whichever is appropriate) level *q*, is enabled if *p \>= q*.
This rule is at the heart of Log4cxx. It assumes that levels are
ordered. For the standard levels, we have *TRACE \< DEBUG \< INFO \<
-WARN \< ERROR \< FATAL*.
+WARN \< ERROR \< FATAL*.
-Here is an example of this rule.
+Here is an example of this rule.
~~~{.cpp}
// get a logger instance named "com.foo"
- log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("com.foo"));
-
+ auto logger = log4cxx::Logger::getLogger("com.foo");
+
// Now set its level. Normally you do not need to set the
// level of a logger programmatically. This is usually done
// in configuration files.
logger->setLevel(log4cxx::Level::getInfo());
-
- log4cxx::LoggerPtr barlogger(log4cxx::Logger::getLogger("com.foo.Bar"));
-
+
+ auto barlogger = log4cxx::Logger::getLogger("com.foo.Bar");
+
// This request is enabled, because WARN >= INFO.
LOG4CXX_WARN(logger, "Low fuel level.");
-
+
// This request is disabled, because DEBUG < INFO.
LOG4CXX_DEBUG(logger, "Starting search for nearest gas station.");
-
+
// The logger instance barlogger, named "com.foo.Bar",
// will inherit its level from the logger named
// "com.foo" Thus, the following request is enabled
// because INFO >= INFO.
LOG4CXX_INFO(barlogger. "Located nearest gas station.");
-
+
// This request is disabled, because DEBUG < INFO.
LOG4CXX_DEBUG(barlogger, "Exiting gas station search");
~~~
Calling the *getLogger* method with the same name will always return a
-reference to the exact same logger object.
+reference to the exact same logger object.
-For example, in
+For example, in
~~~{.cpp}
- log4cxx::LoggerPtr x = log4cxx::Logger::getLogger("wombat");
- log4cxx::LoggerPtr y = log4cxx::Logger::getLogger("wombat");
+ auto x = log4cxx::Logger::getLogger("wombat");
+ auto y = log4cxx::Logger::getLogger("wombat");
~~~
-*x* and *y* refer to *exactly* the same logger object.
+*x* and *y* refer to *exactly* the same logger object.
Thus, it is possible to configure a logger and then to retrieve the same
instance somewhere else in the code without passing around references.
In fundamental contradiction to biological parenthood, where parents
always preceed their children, Log4cxx loggers can be created and
configured in any order. In particular, a "parent" logger will find and
-link to its descendants even if it is instantiated after them.
+link to its descendants even if it is instantiated after them.
Configuration of the Log4cxx environment is typically done at
application initialization. The preferred way is by reading a
-configuration file. This approach will be discussed shortly.
+configuration file. This approach will be discussed shortly.
Log4cxx makes it easy to name loggers by *software component*. This can
be accomplished by statically instantiating a logger in each class, with
@@ -256,10 +256,10 @@ output bears the name of the generating logger, this
naming strategy
makes it easy to identify the origin of a log message. However, this is
only one possible, albeit common, strategy for naming loggers. Log4cxx
does not restrict the possible set of loggers. The developer is free to
-name the loggers as desired.
+name the loggers as desired.
Nevertheless, naming loggers after the class where they are located
-seems to be the best strategy known so far.
+seems to be the best strategy known so far.
# Appenders and Layouts {#appenders-and-layouts}
@@ -268,7 +268,7 @@ their logger is only part of the picture. Log4cxx allows
logging
requests to print to multiple destinations. In Log4cxx speak, an output
destination is called an *appender*. Currently, appenders exist for the
[console](@ref log4cxx.ConsoleAppender), [files](@ref log4cxx.FileAppender),
-GUI components, [remote socket](@ref log4cxx.net.SocketAppender)
+GUI components, [remote socket](@ref log4cxx.net.XMLSocketAppender)
servers, [NT Event Loggers](@ref log4cxx.nt.NTEventLogAppender),
and remote UNIX [Syslog](@ref log4cxx.net.SyslogAppender)
daemons. It is also possible to log
@@ -292,16 +292,16 @@ appender accumulation is no longer additive by
The rules governing appender additivity are summarized below.
-**Appender Additivity**
+## Appender Additivity {#additivity}
The output of a log statement of logger *C* will go to all the appenders
in *C* and its ancestors. This is the meaning of the term "appender
additivity". However, if an ancestor of logger *C*, say *P*, has the
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,
+not the appenders in any of the ancestors of *P*.
+
+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
@@ -324,15 +324,15 @@ destination but also the output format. This is
accomplished by
associating a *layout* with an appender. The layout is responsible for
formatting the logging request according to the user's wishes, whereas
an appender takes care of sending the formatted output to its
-destination.
+destination.
The [PatternLayout](@ref log4cxx.PatternLayout),
part of the standard Log4cxx distribution, lets the user specify the
output format according to conversion patterns similar to the C language
-*printf* function.
+*printf* function.
For example, the PatternLayout with the conversion pattern `%%r [%%t]
-%%-5p %%c - %%m%%n` will output something akin to:
+%%-5p %%c - %%m%%n` will output something akin to:
~~~
176 [main] INFO org.foo.Bar - Located nearest gas station.
@@ -342,7 +342,7 @@ The first field is the number of milliseconds elapsed since
the start of
the program. The second field is the thread making the log request. The
third field is the level of the log statement. The fourth field is the
name of the logger associated with the log request. The text after the
-'-' is the message of the statement.
+'-' is the message of the statement.
# Configuration {#configuration}
@@ -351,87 +351,72 @@ of planning and effort. Observation shows that
approximately 4 percent
of code is dedicated to logging. Consequently, even moderately sized
applications will have thousands of logging statements embedded within
their code. Given their number, it becomes imperative to manage these
-log statements without the need to modify them manually.
+log statements without the need to modify them manually.
The Log4cxx environment is fully configurable programmatically. However,
it is far more flexible to configure Log4cxx using configuration files.
Currently, configuration files can be written in XML or in Java
-properties (key=value) format.
+properties (key=value) format.
Let us give a taste of how this is done with the help of an imaginary
-application *MyApp* that uses Log4cxx.
+application *MyApp* that uses Log4cxx.
-~~~{.cpp}
- #include "com/foo/bar.h"
- using namespace com::foo;
-
- // include log4cxx header files.
- #include "log4cxx/logger.h"
- #include "log4cxx/basicconfigurator.h"
- #include "log4cxx/helpers/exception.h"
-
- using namespace log4cxx;
- using namespace log4cxx::helpers;
-
- LoggerPtr logger(Logger::getLogger("MyApp"));
-
- int main(int argc, char **argv)
- {
- int result = EXIT_SUCCESS;
- try
- {
- // Set up a simple configuration that logs on the console.
- BasicConfigurator::configure();
-
- LOG4CXX_INFO(logger, "Entering application.");
- Bar bar;
- bar.doIt();
- LOG4CXX_INFO(logger, "Exiting application.");
- }
- catch(Exception&)
- {
- result = EXIT_FAILURE;
- }
-
- return result;
- }
-~~~
+## A Simple Example {#example1}
-*MyApp* begins by including Log4cxx headers. It then defines a static
-logger variable with the name *MyApp* which happens to be the fully
-qualified name of the class.
+In order to start using Log4cxx, a simple example program is shown below.
+This program does nothing useful, but it shows the basics of how to start
using Log4cxx.
+Using the [BasicConfigurator](@ref log4cxx.BasicConfigurator) class, we are
able to quickly configure the library
+to output DEBUG, INFO, etc level messages to standard output.
+\include MyApp1.cpp
-*MyApp* uses the *Bar* class defined in header file *com/foo/bar.h*.
-~~~{.cpp}
- // file com/foo/bar.h
- #include "log4cxx/logger.h"
-
- namespace com {
- namespace foo {
- class Bar {
- static log4cxx::LoggerPtr logger;
-
- public:
- void doIt();
- };
- }
- }
-~~~
+The above application does nothing useful except to show how to initialize
logging
+with the BasicConfigurator and do logging with different loggers.
+Note that file based configurations are also possible -
+see [DOMConfigurator](@ref log4cxx.xml.DOMConfigurator.configure)
+and [PropertyConfigurator](@ref log4cxx.PropertyConfigurator.configure).
-~~~{.cpp}
- // file bar.cpp
- #include "com/foo/bar.h"
-
- using namespace com::foo;
- using namespace log4cxx;
-
- LoggerPtr Bar::logger(Logger::getLogger("com.foo.bar"));
-
- void Bar::doIt() {
- LOG4CXX_DEBUG(logger, "Did it again!");
- }
-~~~
+Configuring Log4cxx in the main function has the limitation that
+any logging statements in static initialization code will not generate output.
+Log4cxx must be configured before it is used and
+in this example Log4cxx is not configured until the main() function starts.
+
+## A Less Simple Example {#example2}
+
+In this example we use a *getLogger()* wrapper function
+which configures Log4cxx on the first usage.
+The advantages of this approach are:
+
+- Log4cxx configuration can be reused in multiple applications.
+- The structure exhibits better [separation of
concerns](https://en.wikipedia.org/wiki/Separation_of_concerns).
+- Log statements in static initialization code will generate output.
+
+This program (*MyApp*) begins by including the file
+that defines the com::foo::getLogger() function.
+It obtains a logger named *MyApp*
+(which in this example is the fully qualified name)
+from the com::foo::getLogger() function.
+
+*MyApp* uses the *com::foo::Bar* class defined in header file *com/foo/bar.h*.
+\include MyApp2.cpp
+
+The *com::foo::Bar* class is defined in header file *com/foo/bar.h*.
+\include com/foo/bar.h
+
+The *com::foo::Bar* class is implemented in the file *com/foo/bar.cpp*.
+\include com/foo/bar.cpp
+
+The header file *com/foo/config.h* defines the com::foo::getLogger() function
+and a *LoggerPtr* type for convenience.
+\include com/foo/config.h
+
+The file *com/foo/config.cpp* which implements the com::foo::getLogger()
function
+defines *initAndShutdown* as a *static struct* so its constructor
+is invoked on the first call to the com::foo::getLogger() function
+and its destructor is automatically called during application exit.
+\include com/foo/config1.cpp
+
+## Request Formatting {#formatting}
The invocation of the
[BasicConfigurator::configure](@ref log4cxx.BasicConfigurator.configure)
@@ -439,164 +424,101 @@ method creates a rather simple Log4cxx setup. This
method is hardwired
to add to the root logger a [ConsoleAppender](@ref log4cxx.ConsoleAppender).
The output will be formatted using a
[PatternLayout](@ref log4cxx.PatternLayout)
-set to the pattern `%%-4r [%%t] %%-5p %%c %%x - %%m%%n`.
+set to the pattern `%%r [%%t] %%p %%c %%x - %%m%%n`.
-Note that by default, the root logger is assigned to
-*Level::getDebug()*.
+Note that by default, the root logger is assigned a *DEBUG* level.
-The output of MyApp is:
+The output of MyApp is:
~~~
- 0 [12345] INFO MyApp - Entering application.
- 36 [12345] DEBUG com.foo.Bar - Did it again!
- 51 [12345] INFO MyApp - Exiting application.
+ 0 [12345] INFO MyApp null - Entering application.
+ 0 [12345] DEBUG com.foo.Bar null - Did it again!
+ 0 [12345] INFO MyApp null - Exiting application.
~~~
The previous example always outputs the same log information.
-Fortunately, it is easy to modify *MyApp* so that the log output can be
-controlled at run-time. Here is a slightly modified version.
+Fortunately, it is easy to modify *config.cpp* so that the log output can be
+controlled at runtime. Here is a slightly modified version.
+\include com/foo/config2.cpp
-~~~{.cpp}
- // file MyApp2.cpp
-
- #include "com/foo/bar.h"
- using namespace com::foo;
-
- // include log4cxx header files.
- #include "log4cxx/logger.h"
- #include "log4cxx/basicconfigurator.h"
- #include "log4cxx/propertyconfigurator.h"
- #include "log4cxx/helpers/exception.h"
-
- using namespace log4cxx;
- using namespace log4cxx::helpers;
- // Define a static logger variable so that it references the
- // Logger instance named "MyApp".
- LoggerPtr logger(Logger::getLogger("MyApp"));
-
- int main(int argc, char **argv)
- {
- int result = EXIT_SUCCESS;
- try
- {
- if (argc > 1)
- {
- // BasicConfigurator replaced with PropertyConfigurator.
- PropertyConfigurator::configure(argv[1]);
- }
- else
- {
- BasicConfigurator::configure();
- }
-
- LOG4CXX_INFO(logger, "Entering application.");
- Bar bar;
- bar.doIt();
- LOG4CXX_INFO(logger, "Exiting application.");
- }
- catch(Exception&)
- {
- result = EXIT_FAILURE;
- }
-
- return result;
- }
-~~~
-
-This version of *MyApp* instructs *PropertyConfigurator* to parse a
-configuration file and set up logging accordingly.
+This version of *config.cpp* instructs [PropertyConfigurator](@ref
log4cxx.PropertyConfigurator.configure)
+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 UserLib/logmanager.cpp file for how to do this).
-Here is a sample configuration file that results in exactly same output
-as the previous *BasicConfigurator* based example.
+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.
~~~
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1
-
+
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
-
+
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
~~~
It can be noticed that the PropertyConfigurator file format is the same
-as log4j.
+as log4j.
Suppose we are no longer interested in seeing the output of any
component belonging to the *com::foo* package. The following
-configuration file shows one possible way of achieving this.
+configuration file shows one possible way of achieving this.
~~~
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
-
+
# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
-
+
# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.foo=WARN
~~~
-The output of *MyApp* configured with this file is shown below.
+The output of *MyApp* configured with this file is shown below.
~~~
- 2000-09-07 14:07:41,508 [12345] INFO MyApp - Entering application.
- 2000-09-07 14:07:41,529 [12345] INFO MyApp - Exiting application.
+ 2022-12-13 11:01:45,091 [12345] INFO MyApp - Entering application.
+ 2022-12-13 11:01:45,091 [12345] INFO MyApp - Exiting application.
~~~
As the logger *com.foo.Bar* does not have an assigned level, it inherits
its level from *com.foo*, which was set to WARN in the configuration
-file. The log statement from the *Bar::doIt* method has the level DEBUG,
+file. The log statement from the *Bar::doIt* method has the level *DEBUG*,
lower than the logger level WARN. Consequently, *doIt()* method's log
-request is suppressed.
-
-Here is another configuration file that uses multiple appenders.
+request is suppressed.
-~~~
- log4j.rootLogger=debug, stdout, R
-
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-
- # Pattern to output the caller's file name and line number.
- log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
-
- log4j.appender.R=org.apache.log4j.RollingFileAppender
- log4j.appender.R.File=example.log
-
- log4j.appender.R.MaxFileSize=100KB
- # Keep one backup file
- log4j.appender.R.MaxBackupIndex=1
-
- log4j.appender.R.layout=org.apache.log4j.PatternLayout
- log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
-~~~
+Here is another configuration file that uses multiple appenders.
+\include MyApp.properties
Calling the enhanced MyApp with the this configuration file will output
-the following on the console.
+the following on the console.
~~~
- INFO [12345] (MyApp2.cpp:31) - Entering application.
- DEBUG [12345] (Bar.h:16) - Doing it again!
- INFO [12345] (MyApp2.cpp:34) - Exiting application.
+ INFO [12345] (MyApp.cpp:8) - Entering application.
+ DEBUG [12345] (bar.cpp:8) - Did it again!
+ INFO [12345] (MyApp.cpp:11) - Exiting application.
~~~
In addition, as the root logger has been allocated a second appender,
output will also be directed to the *example.log* file. This file will
be rolled over when it reaches 100KB. When roll-over occurs, the old
-version of *example.log* is automatically moved to *example.log.1*.
+version of *example.log* is automatically moved to *example.log.1*.
Note that to obtain these different logging behaviors we did not need to
recompile code. We could just as easily have logged to a UNIX Syslog
daemon, redirected all *com.foo* output to an NT Event logger, or
forwarded logging events to a remote Log4cxx server, which would log
according to local server policy, for example by forwarding the log
-event to a second Log4cxx server.
+event to a second Log4cxx server.
-# Default Initialization Procedure {#default-initialization-procedure}
+## Default Initialization {#default-initialization}
The Log4cxx library does not make any assumptions about its environment.
In particular, when initially created the root [Logger](@ref log4cxx.Logger)
has no appender.
@@ -611,7 +533,7 @@ of [LoggerRepository](@ref log4cxx.spi.LoggerRepository).
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.
+A full example can be seen in the \ref UserLib/logmanager.cpp file.
# Internal Debugging {#internal-debugging}
@@ -636,17 +558,17 @@ especially well suited to trace and debug complex
distributed
applications. A common approach to differentiate the logging output of
one client from another is to instantiate a new separate logger for each
client. This promotes the proliferation of loggers and increases the
-management overhead of logging.
+management overhead of logging.
A lighter technique is to uniquely stamp each log request initiated from
the same client interaction. Neil Harrison described this method in the
book "Patterns for Logging Diagnostic Messages," in *Pattern Languages
of Program Design 3*, edited by R. Martin, D. Riehle, and F. Buschmann
-(Addison-Wesley, 1997).
+(Addison-Wesley, 1997).
To uniquely stamp each request, the user pushes contextual information
into the NDC, the abbreviation of *Nested Diagnostic Context*. The NDC
-class is shown below.
+class is shown below.
~~~{.cpp}
namespace log4cxx {
@@ -655,10 +577,10 @@ class is shown below.
// pushes the value on construction and pops on
destruction.
NDC(const std::string& value);
NDC(const std::wstring& value);
-
+
// Remove the top of the context from the NDC.
static LogString pop();
-
+
// Add diagnostic context for the current thread.
static void push(const std::string& message);
static void push(const std::wstring& message);
@@ -674,7 +596,7 @@ the current thread in the log output. This is done without
the
intervention of the user, who is responsible only for placing the
correct information in the NDC by using the *push* and *pop* methods at
a few well-defined points in the code. In contrast, the per-client
-logger approach commands extensive changes in the code.
+logger approach commands extensive changes in the code.
To illustrate this point, let us take the example of a servlet
delivering content to numerous clients. The servlet can build the NDC at
@@ -686,14 +608,14 @@ simultaneously, the logs initiated by the same code, i.e.
belonging to
the same logger, can still be distinguished because each client request
will have a different NDC stack. Contrast this with the complexity of
passing a freshly instantiated logger to all code exercised during the
-client's request.
+client's request.
Nevertheless, some sophisticated applications, such as virtual hosting
web servers, must log differently depending on the virtual host context
and also depending on the software component issuing the request. Recent
Log4cxx releases support multiple hierarchy trees. This enhancement
allows each virtual host to possess its own copy of the logger
-hierarchy.
+hierarchy.
# Performance {#performance}
@@ -701,12 +623,12 @@ One of the often-cited arguments against logging is its
computational
cost. This is a legitimate concern as even moderately sized applications
can generate thousands of log requests. Much effort was spent measuring
and tweaking logging performance. Log4cxx claims to be fast and
-flexible: speed first, flexibility second.
+flexible: speed first, flexibility second.
-The user should be aware of the following performance issues.
+The user should be aware of the following performance issues.
+
+1. **Logging performance when logging is turned off.**
-1. **Logging performance when logging is turned off.**
-
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.
@@ -724,20 +646,20 @@ The user should be aware of the following performance
issues.
true for appenders.
3. **The cost of changing a logger's level.**
-
+
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
existing ancestors. In the *BasicConfigurator* example shown
earlier, the logger named *com.foo.Bar* is linked directly to the
root logger, thereby circumventing the nonexistent *com* or
*com.foo* loggers. This significantly improves the speed of the
- walk, especially in "sparse" hierarchies.
+ walk, especially in "sparse" hierarchies.
-# Removing log statements {#removing-log-statements}
+## Removing log statements {#removing-log-statements}
Sometimes, you may want to remove all log statements from your program,
either for speed purposes or to remove sensitive information. This can easily
@@ -772,7 +694,7 @@ The levels are set as follows:
Note that this has no effect on other macros, such as using the
`LOG4CXX_LOG`, `LOG4CXX_LOGLS`, or `LOG4CXX_L7DLOG` family of macros.
-# Removing location information {#removing-location-information}
+## Removing location information {#removing-location-information}
Whenever you log a message with Log4cxx, metadata about the location of the
logging statement is captured as well through the preprocessor. This includes
@@ -846,7 +768,7 @@ As with the standard logger macros, these macros will also
be compiled out
if the `LOG4CXX_THRESHOLD` macro is set to a level that will compile out
the non-FMT macros.
-A full example can be seen in the src/examples/cpp/format-string.cpp file.
+A full example can be seen in the \ref format-string.cpp file.
# Filtering Messages {#filtering}
@@ -864,7 +786,7 @@ message means that the message will be logged immediately
without
consulting other filters. Denying has the opposite affect, immediately
dropping the log message and not consulting any other filters.
-See the documentation for [Filter](@ref log4cxx.Filter) for some more
+See the documentation for [Filter](@ref log4cxx.spi.Filter) for some more
information, or view a [configuration sample](@ref configuration-samples).
# Conclusions {#conclusions}
@@ -873,7 +795,7 @@ Apache Log4cxx is a popular logging package written in C++.
One of its
distinctive features is the notion of inheritance in loggers. Using a
logger hierarchy it is possible to control which log statements are
output at arbitrary granularity. This helps reduce the volume of logged
-output and minimize the cost of logging.
+output and minimize the cost of logging.
One of the advantages of the Log4cxx API is its manageability. Once the
log statements have been inserted into the code, they can be controlled
@@ -881,3 +803,12 @@ with configuration files. They can be selectively enabled
or disabled,
and sent to different and multiple output targets in user-chosen
formats. The Log4cxx package is designed so that log statements can
remain in shipped code without incurring a heavy performance cost.
+
+\example auto-configured.cpp
+This is an example of logging in static initialization code.
+
+\example UserLib/logmanager.cpp
+This file is an example of how to use the current module name to select the
Log4cxx configuration file.
+
+\example format-string.cpp
+This example shows logging using the
[{fmt}](https://fmt.dev/latest/index.html) library.