中文版本 | English Edition
- log4cpp
log4cpp is a C++ logging library inspired by log4j
Features:
- Configurable via JSON files, no code modification required
- Supports logging to STDOUT and STDERR
- Supports logging to specified files
- Supports logging to log server (TCP/UDP)
- Singleton pattern
- Thread-safe
- Hot configuration reload, changes take effect without restarting the process(Linux only)
- C++ compiler supporting C++17 or later
- CMake 3.11 or later
- nlohmann-json >= 3.7
To install nlohmann-json on Ubuntu/Debian:
sudo apt install nlohmann-json3-devCMakeLists.txt:
cmake_minimum_required(VERSION 3.11)
project(log4cpp-demo)
set(TARGET_NAME demo)
add_executable(${TARGET_NAME} main.cpp)
include(FetchContent)
FetchContent_Declare(log4cpp GIT_REPOSITORY https://github.com/lwhttpdorg/log4cpp.git GIT_TAG v4.0.2)
FetchContent_MakeAvailable(log4cpp)
target_link_libraries(${TARGET_NAME} log4cpp)Header file:
#include <log4cpp/log4cpp.hpp>3.1.3. Load Configuration File (Optional)
Configuration can be loaded in two ways:
- If
log4cpp.jsonexists in the current path, it will be loaded automatically - If the configuration file is not in the current path, or has a different name, you need to load it manually
Notes: If log4cpp.json does not exist and is not loaded manually, the built-in default configuration will be used.
const std::string config_file = "demo.json";
auto &log_mgr = log4cpp::supervisor::get_logger_manager();
log_mgr.load_config(config_file);Get the configured logger by name
std::shared_ptr<log4cpp::log::logger> log = log4cpp::logger_manager::get_logger(const std::string &name = "root");You can specify a unique string, which can be output to the log (the length of the output can be specified via
${<n>NM} in "log-pattern")
hello : 2025-11-13 23:32:02:475 [main ] [ERROR] -- this is an errorAfter getting the logger, you can use the following methods to output the log:
void trace(const char *__restrict fmt, ...);
void debug(const char *__restrict fmt, ...);
void info(const char *__restrict fmt, ...);
void warn(const char *__restrict fmt, ...);
void error(const char *__restrict fmt, ...);
void fatal(const char *__restrict fmt, ...);Or directly:
void log(log_level level, const char *fmt, ...);The log level log_level level is defined as follows:
namespace log4cpp {
enum class log_level { FATAL, ERROR, WARN, INFO, DEBUG, TRACE };
}Description:
FATAL: Fatal errorERROR: ErrorWARN: WarningINFO: InformationDEBUG: DebuggingTRACE: Tracing
The logger object can also be used as a class member variable (or static member variable). Since it is a
std::shared_ptr, all instances of the class will use the same logger
class demo {
public:
demo() {
logger = log4cpp::logger_manager::get_logger("demo");
logger->info("constructor");
}
~demo() {
logger->info("destructor");
}
void func(const std::string &name) const {
logger->info("func(%s)", name.c_str());
}
private:
std::shared_ptr<log4cpp::log::logger> logger;
};You will get the following log:
demo: 2025-11-29 20:06:47:652 [main ] [INFO ] -- constructor
demo: 2025-11-29 20:06:47:652 [main ] [INFO ] -- func(hello)
demo: 2025-11-29 20:06:47:652 [main ] [INFO ] -- destructor#include <thread>
#include <log4cpp/log4cpp.hpp>
class demo {
public:
demo() {
logger = log4cpp::logger_manager::get_logger("demo");
logger->info("constructor");
}
~demo() {
logger->info("destructor");
}
void func(const std::string &name) const {
logger->info("func(%s)", name.c_str());
}
private:
std::shared_ptr<log4cpp::log::logger> logger;
};
void thread_routine() {
log4cpp::set_thread_name("child");
const auto log = log4cpp::logger_manager::get_logger("aaa");
for (int i = 0; i < 10; ++i) {
log->trace("this is a trace");
log->debug("this is a debug");
log->info("this is a info");
log->warn("this is an warning");
log->error("this is an error");
log->fatal("this is a fatal");
}
}
int main() {
#ifndef _WIN32
log4cpp::supervisor::enable_config_hot_loading();
#endif
const std::string config_file = "demo.json";
auto &log_mgr = log4cpp::supervisor::get_logger_manager();
log_mgr.load_config(config_file);
std::thread child(thread_routine);
log4cpp::set_thread_name("main");
const auto log = log4cpp::logger_manager::get_logger("hello");
for (int i = 0; i < 10; ++i) {
log->trace("this is a trace");
log->debug("this is a debug");
log->info("this is a info");
log->warn("this is an warning");
log->error("this is an error");
log->fatal("this is a fatal");
}
child.join();
demo app;
app.func("hello");
return 0;
}Example Log Output:
root : 2025-11-13 23:32:02:475 [child ] [ERROR] -- this is an error
hello : 2025-11-13 23:32:02:475 [main ] [ERROR] -- this is an error
root : 2025-11-13 23:32:02:475 [child ] [FATAL] -- this is a fatal
hello : 2025-11-13 23:32:02:475 [main ] [FATAL] -- this is a fatal
root : 2025-11-13 23:32:02:475 [child ] [INFO ] -- this is a info
hello : 2025-11-13 23:32:02:475 [main ] [INFO ] -- this is a info
root : 2025-11-13 23:32:02:475 [child ] [WARN ] -- this is an warning
hello : 2025-11-13 23:32:02:475 [main ] [WARN ] -- this is an warning
root : 2025-11-13 23:32:02:475 [child ] [ERROR] -- this is an error
hello : 2025-11-13 23:32:02:475 [main ] [ERROR] -- this is an error
root : 2025-11-13 23:32:02:475 [child ] [FATAL] -- this is a fatalConfiguration File Example:
Reference configuration file demo/demo.json
{
"log-pattern": "${NM}: ${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}:${ms} [${8TH}] [${L}] -- ${msg}"
}Placeholders:
${<n>NM}: Logger name, e.g.${8NM}.<n>is the logger name length, left-aligned, default is 6, max is 64${yy}: Year represented by 2 digits. e.g. 99 or 03${yyyy}: Full year, at least 4 digits, using '-' for BC e.g. -0055, 0787, 1999, 2003, 10191${M}: Month in number, without leading zero. From 1 to 12${MM}: Month in number, two digits with leading zero. From 01 to 12${MMM}: Abbreviated month name, 3 letters. From Jan to Dec${d}: Day of the month, without leading zero. From 1 to 31${dd}: Day of the month, two digits with leading zero. From 01 to 31${h}: Hour in 12-hour clock without leading zero. AM and PM for morning and afternoon. From 0 to 12${hh}: Hour in 12-hour clock with leading zero. AM and PM for morning and afternoon. From 00 to 12${H}: Hour in 24-hour clock without leading zero. From 0 to 23${HH}: Hour in 24-hour clock with leading zero. From 00 to 23${m}: Minute without leading zero. From 1 to 59${mm}: Minute with leading zero. From 01 to 59${s}: Second without leading zero. From 1 to 59${ss}: Second with leading zero. From 01 to 59${ms}: Millisecond with leading zero. From 001 to 999${<n>TN}: is the thread name length, left-aligned, default is 16, max is 16. If the thread name is empty, "T+$ {Thread ID}" is used instead, e.g., "main", "T12345"${<n>TH}: Thread id, e.g.${8TH}.<n>is the number of digits for the Thread ID, left-padded with 0, default is 8, max is 8. e.g. "T12345"${L}: Log level, Value range: FATAL, ERROR, WARN, INFO, DEBUG, TRACE${msg}: Log message body, e.g. hello world!
Note: Some systems cannot set thread names, and multiple threads can only be distinguished by Thread ID
Note: The default log-pattern is "${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss} [${8TN}] [${L}] -- ${msg}"
There are four types of appenders: Console Appender(console), File Appender(file), Socket Appender(socket, default
is TCP)
A simple configuration file example:
{
"appenders": {
"console": {
"out-stream": "stdout"
},
"file": {
"file-path": "log/log4cpp.log"
},
"socket": {
"host": "10.0.0.1",
"port": 9443,
"protocol": "tcp",
"prefer-stack": "auto"
}
}
}The Console Appender's function is to output logs to STDOUT or STDERR. Typical configuration is as follows:
{
"appenders": {
"console": {
"out-stream": "stdout"
}
}
}Description:
out-stream: Output stream, can be "stdout" or "stderr"
The File Appender's function is to output logs to a specified file. Typical configuration is as follows:
{
"appenders": {
"file": {
"file-path": "log/log4cpp.log"
}
}
}Description:
file-path: output file name
The Socket Appender supports both TCP and UDP protocols, distinguished by the protocol field. If protocol is not
configured, it defaults to TCP
{
"appenders": {
"socket": {
"host": "10.0.0.1",
"port": 9443,
"protocol": "tcp",
"prefer-stack": "auto"
}
}
}Description:
host: Remote log server hostnameport: Remote log server portprotocol: Protocol, can be "tcp" or "udp", default is "tcp"prefer-stack: Preferred address stack, can be "IPv4", "IPv6", or "auto", default is "AUTO"
Notes: For TCP-type socket appender, if the connection to the remote logging server fails, it will attempt to reconnect with exponential backoff until the connection succeeds
loggers is an array. Each logger configuration includes:
name: Logger name, used to retrieve the logger, must be unique.rootis the default loggerlevel: Log level. Only logs greater than or equal to this level will be output. Can be omitted for non-rootloggers (automatically inherits fromroot)appenders: Appenders. Only configured appenders will output logs. Appenders can beconsole,file,socket. Can be omitted for non-rootloggers (automatically inherits fromroot)
The default logger must be defined with name root
{
"loggers": [
{
"name": "root",
"level": "INFO",
"appenders": [
"console",
"file"
]
},
{
"name": "hello",
"level": "INFO",
"appenders": [
"console",
"socket"
]
},
{
"name": "aaa",
"level": "WARN",
"appenders": [
"file"
]
}
]
}Configuration hot reloading allows changes to the configuration file to take effect without restarting the process ( Linux system only)
Note: The configuration file path and name cannot be changed; the path and name used at startup will be reloaded.
First, you need to enable configuration hot loading:
log4cpp::supervisor::enable_config_hot_loading(int sig = SIGHUP);After modifying the configuration file, send a signal to your process (default is SIGHUP):
kill -SIGHUP <PID>The SIGHUP signal will trigger log4cpp to reload the configuration file using the cached path and filename, and
recreate internal objects. The std::shared_ptr<log4cpp::log::logger> previously obtained via
log4cpp::logger_manager::get_logger() will not become invalid and can continue to be used
_Note: The std::shared_ptr returned by log4cpp::logger_manager::get_logger() may not change, even if its internal
proxy object has changed
MingW64:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_LOG4CPP_DEMO=ON -DBUILD_LOG4CPP_TEST=ON -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH="D:/OpenCode/nlohmann_json"MSVC:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_LOG4CPP_DEMO=ON -DBUILD_LOG4CPP_TEST=ON -G "Visual Studio 17 2022" -A x64 -DCMAKE_PREFIX_PATH="D:/OpenCode/nlohmann_json"cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_LOG4CPP_DEMO=ON -DBUILD_LOG4CPP_TEST=ON -DENABLE_ASAN=ONOptions:
DBUILD_LOG4CPP_DEMO=ON: Build demo, defaultOFF(not build)DBUILD_LOG4CPP_TEST=ON: Build test programs , defaultOFF(not build)DENABLE_ASAN=ON: Enable AddressSanitizer, defaultOFF(not enabled)
cmake --build build -j $(nproc)This project uses Google Test for unit testing
ctest --test-dir build --output-on-failureOr enable more verbose output from tests:
ctest --test-dir build --verboseIf your code modifies existing functionality, please ensure that ASAN detection passes. Code that has not passed ASAN detection will not be merged
This project is licensed under LGPLv3