Skip to content

lwhttpdorg/log4cpp

Repository files navigation

log4cpp


中文版本 | English Edition

1. What is 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)

2. Requirements

  1. C++ compiler supporting C++17 or later
  2. CMake 3.11 or later
  3. nlohmann-json >= 3.7

3. Usage

3.1. Quick Start

To install nlohmann-json on Ubuntu/Debian:

sudo apt install nlohmann-json3-dev

3.1.1. Create a CMake Project

CMakeLists.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)

3.1.2. Include Header File

Header file:

#include <log4cpp/log4cpp.hpp>

3.1.3. Load Configuration File (Optional)

Configuration can be loaded in two ways:

  • If log4cpp.json exists 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);

3.1.3. Get Logger

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 error

3.1.4. Output Log

After 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 error
  • ERROR: Error
  • WARN: Warning
  • INFO: Information
  • DEBUG: Debugging
  • TRACE: Tracing

3.1.5. Use in a Class

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

3.1.6. Complete Example

#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 fatal

Configuration File Example:

Reference configuration file demo/demo.json

3.2. Advanced Usage

3.2.1. Configuration File

3.2.1.1. Log pattern
{
	"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}"

3.2.1.2. Appender

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"
		}
	}
}
3.2.1.2.1. Console Appender

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"
3.2.1.2.2. File Appender

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

3.2.2. Socket appender

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 hostname
  • port: Remote log server port
  • protocol: 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

3.2.3. Logger

loggers is an array. Each logger configuration includes:

  • name: Logger name, used to retrieve the logger, must be unique. root is the default logger
  • level: Log level. Only logs greater than or equal to this level will be output. Can be omitted for non-root loggers (automatically inherits from root)
  • appenders: Appenders. Only configured appenders will output logs. Appenders can be console, file, socket. Can be omitted for non-root loggers (automatically inherits from root)

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"
			]
		}
	]
}

3.3. Hot Configuration Reload

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

4. Building

4.1. Configuration

4.1.1. Windows

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"

4.1.2. Linux

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_LOG4CPP_DEMO=ON -DBUILD_LOG4CPP_TEST=ON -DENABLE_ASAN=ON

Options:

  • DBUILD_LOG4CPP_DEMO=ON: Build demo, default OFF (not build)
  • DBUILD_LOG4CPP_TEST=ON: Build test programs , default OFF (not build)
  • DENABLE_ASAN=ON: Enable AddressSanitizer, default OFF (not enabled)

4.2. Build

cmake --build build -j $(nproc)

4.3. Testing

This project uses Google Test for unit testing

ctest --test-dir build --output-on-failure

Or enable more verbose output from tests:

ctest --test-dir build --verbose

4.4. ASAN

If your code modifies existing functionality, please ensure that ASAN detection passes. Code that has not passed ASAN detection will not be merged

4.5. License

This project is licensed under LGPLv3

About

Logger for C++

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published