中文版本 | English Edition
log4cpp is a C++ logging library inspired by log4j.
Features:
- JSON configuration: change behavior without modifying code
- Output logs to STDOUT and STDERR
- Output logs to files
- Output logs to TCP clients
- Output logs to UDP clients
- Singleton pattern
- Thread-safe
- Hot-reload configuration without restarting the process
- C++ compiler supporting C++17 or newer
- CMake 3.11 or newer
- nlohmann-json >= 3.7
Warning: Due to a series of bugs in the MSVC compiler, this project no longer supports MSVC. Any errors on the MSVC platform will no longer be fixed. It is recommended to use MingW64
sudo apt install nlohmann-json3-devinclude(FetchContent)
FetchContent_Declare(log4cpp GIT_REPOSITORY https://github.com/lwhttpdorg/log4cpp.git GIT_TAG v3.0.8)
FetchContent_MakeAvailable(log4cpp)
target_link_libraries(${YOUR_TARGET_NAME} log4cpp)Example:
{
"log_pattern": "${NM}: ${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}:${ms} [${8TH}] [${L}] -- ${W}"
}Placeholders:
${<n>NM}: The name of logger, e.g.${4NM}.<n>is the name length, align left, default is 6, max width is 16${yy}: A two digit representation of a year. e.g. 99 or 03${yyyy}: A full numeric representation of a year, at least 4 digits, with - for years BCE. e.g. -0055, 0787, 1999, 2003, 10191${M}: Numeric representation of a month, without leading zeros. 1 through 12${MM}: Numeric representation of a month, with leading zeros. 01 through 12${MMM}: A short textual representation of a month, three letters. Jan through Dec${d}: Day of the month without leading zeros. 1 to 31${dd}: Day of the month, 2 digits with leading zeros. 01 to 31${h}: 12-hour format of an hour without leading zeros, with Uppercase Ante meridiem and Post meridiem, 0 through 12${hh}: 12-hour format of an hour with leading zeros, with Uppercase Ante meridiem and Post meridiem, 00 through 12${H}: 24-hour format of an hour without leading zeros. 0 through 23${HH}: 24-hour format of an hour with leading zeros. 00 through 23${m}: Minutes without leading zeros. 1 to 59${mm}: Minutes with leading zeros. 01 to 59${s}: Seconds without leading zeros. 1 to 59${ss}: Seconds with leading zeros. 01 to 59${ms}: Milliseconds with leading zeros. 001 to 999${<n>TN}: Thread name. e.g.${8TN}.<n>is the name length, align left, default is 16, max width is 16. If the name is empty, use "T+thread id" instead. e.g. "main"${<n>TH}: Thread id, e.g.${8TH}.<n>is the digit width, left padding with 0, default is 8, max width is 8. e.g. "T12345"${L}: Log level, Value range: FATAL, ERROR, WARN, INFO, DEBUG, TRACE${W}: Log message body, e.g. hello world!
Note: Some systems cannot set thread names; thread ID will be used instead.
There are four types of appenders: Console appender(console), File appender(file), TCP appender(tcp), UDP
appender(udp)
Example:
{
"appenders": {
"console": {
"out_stream": "stdout"
},
"file": {
"file_path": "log/log4cpp.log"
},
"tcp": {
"local_addr": "0.0.0.0",
"port": 9443
},
"udp": {
"local_addr": "0.0.0.0",
"port": 9443
}
}
}Outputs logs to STDOUT or STDERR.
Example:
{
"appenders": {
"console": {
"out_stream": "stdout"
}
}
}out_stream: "stdout" or "stderr"
Outputs logs to a specified file.
Example:
{
"appenders": {
"file": {
"file_path": "log/log4cpp.log"
}
}
}file_path: output file path
TCP appender starts a TCP server, accepts connections, and sends logs to connected clients. Typical config:
{
"appenders": {
"tcp": {
"local_addr": "0.0.0.0",
"port": 9443
}
}
}local_addr: listening address (e.g. "0.0.0.0", "::", "127.0.0.1", "::1")port: listening port
Notes:
- When multiple TCP clients are connected, logs are iterated and sent to each client.
- Logs are sent in plaintext; handle encryption externally if required.
UDP appender starts a UDP server and sends logs to clients.
Differences vs TCP:
- UDP is connectionless and cannot guarantee delivery.
- Clients must send a "hello" message so the server learns the client's address.
- Clients should send a "bye" message before exit so the server can remove the client address; otherwise the address remains until failure cleanup or program exit.
Example:
{
"appenders": {
"udp": {
"local_addr": "0.0.0.0",
"port": 9443
}
}
}local_addr: listening addressport: listening port
There are named loggers (loggers) and the root logger (root).
- Named loggers are optional; the root logger must be present.
- If a requested logger name does not exist,
log4cpp::logger_manager::get_loggerreturns the root logger.
Named logger fields:
name: unique logger name (cannot be "root")level: minimum log level — only logs with level >= this will be outputappenders: list of appender names (must match keys defined inappenders), options: "console", "file", "tcp", "udp"
Root logger is an object with level and appenders; internally its name is "root".
Example structure:
{
"root": {
"level": "INFO",
"appenders": [
"console",
"file"
]
},
"loggers": [
{
"name": "hello",
"level": "INFO",
"appenders": [
"console",
"tcp",
"udp"
]
},
{
"name": "lwhttpd.org",
"level": "WARN",
"appenders": [
"file"
]
}
]
}Two ways to load configuration:
- If
log4cpp.jsonexists in the current working directory, it is loaded automatically. - Otherwise, load manually:
const std::string config_file = "log4cpp_config_1.json";
log4cpp::logger_manager &log_mgr = log4cpp::supervisor::get_logger_manager();
log_mgr.load_config(config_file);Hot-reload allows applying configuration changes without restarting (Linux only).
Note: The config file path and name must not change; the original path/name used at startup is reloaded.
Enable hot-reload:
log4cpp::supervisor::enable_config_hot_loading(int sig = SIGHUP);After modifying the configuration file, send a signal (default is SIGHUP) to your process:
kill -SIGHUP <PID>SIGHUP triggers log4cpp to reload the cached file path and recreate internal objects. Existing
std::shared_ptr<log4cpp::log::logger> instances returned earlier remain valid until their reference count reaches
zero.
Note: log4cpp::logger_manager::get_logger() may return the same shared_ptr even if the underlying proxy object
changed.
Include header:
#include <log4cpp/log4cpp.hpp>Get a logger:
std::shared_ptr<log4cpp::log::logger> log = log4cpp::logger_manager::get_logger("aaa");Logging methods:
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 use the generic API:
void log(log_level level, const char *fmt, ...);Log level enum:
namespace log4cpp {
enum class log_level { FATAL, ERROR, WARN, INFO, DEBUG, TRACE };
}Notes:
FATAL: fatal errorERROR: errorWARN: warningINFO: informationDEBUG: debuggingTRACE: tracing
#include <thread>
#include <log4cpp/log4cpp.hpp>
void thread_routine() {
log4cpp::set_thread_name("child");
const auto log = log4cpp::logger_manager::get_logger("aaa");
for (int i = 0; i < 100; ++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() {
log4cpp::supervisor::enable_config_hot_loading();
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 < 100; ++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();
return 0;
}CMakeLists.txt example:
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 v3.2.1)
FetchContent_MakeAvailable(log4cpp)
target_link_libraries(${TARGET_NAME} log4cpp)Sample 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 fatalReference config: demo/demo.json
Contributions welcome. Before submitting a PR, note the following.
cmake -S . -B build -DBUILD_LOG4CPP_DEMO=ON -DBUILD_LOG4CPP_TEST=ON -DENABLE_ASAN=ONcmake --build build --config=Debug -j $(nproc)ctest --test-dir build --output-on-failureOptions:
-DBUILD_LOG4CPP_DEMO=ONbuild demo (off by default)-DBUILD_LOG4CPP_TEST=ONbuild tests (off by default)-DENABLE_ASAN=ONenable AddressSanitizer (off by default)
This project uses Google Test; tests are under the test directory. Add tests as needed. Ensure changes are covered by
tests.
Ensure ASAN passes for changes; PRs that fail ASAN will not be merged.
This project is licensed under LGPLv3 (see LICENSE).