>_ DevTrendsen

Language

Home

Languages

Sections

Frontend Backend Mobile DevOps AI / ML GameDev Blockchain Embedded Security
C-plus-plus

Fast C++ Logging Without the Pain and Heavy Dependencies

Anyone who has written something more complex than tutorial scripts in C++ has faced the choice of a logging library. The standard std::cout is convenient for simple console utilities, but quickly becomes a bottleneck due to slow I/O and lack of convenient formatting. Alternatives like Boost.Log or log4cxx bring along their dependencies, take a long time to compile, and are intimidating with their complex configuration.

In 2014, developer Gabriel Melman published the spdlog library. The project quickly gained popularity in the C++ community and now has almost 30,000 stars on GitHub.

What Makes It Popular

The main reason for the library's success is the balance between performance and ease of integration. spdlog is distributed in header-only format: you just copy the include directory into your project, include the header file, and start working. If you need to speed up your application's build, the library can be pre-compiled via CMake or installed from most popular package managers (apt, brew, vcpkg, conan).

String formatting is built on top of the fmt library, which became the basis for std::format in C++20. This eliminates cumbersome chains with the << operator. Alignment, hexadecimal number output, and positional arguments are written easily and clearly.

Useful Features for Daily Development

Combining Log Sinks

In practice, you often need to send errors to the console with color highlighting, while writing detailed debug information to a file with rotation. In spdlog, this task is solved by combining multiple sinks into a single logger object:

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"

void setup_logging() {
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);

    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/app.txt", true);
    file_sink->set_level(spdlog::level::trace);

    spdlog::logger logger("multi_sink", {console_sink, file_sink});
    logger.info("Сообщение зафиксировано только в файле");
    logger.warn("Сообщение попадет и в консоль, и в файл");
}

Asynchronous Mode for High-Load Tasks

In worker threads of network servers or game engines, direct disk writes delay code execution. spdlog can work asynchronously: a background thread pulls messages from a ring buffer and flushes them to disk without slowing down the main computations.

#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"

void async_example() {
    // Очередь на 8192 элемента и 1 фоновый поток
    spdlog::init_thread_pool(8192, 1); 
    
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(
        "async_logger", "logs/async_log.txt"
    );
    
    async_file->info("Запись лога не блокирует текущий поток");
}

Ring Buffer for Error Tracing

An interesting feature of the library is support for backtrace. Suppose the production environment has the log level set to info and detailed debugging is disabled. When backtrace is activated, the library saves the last N debug messages in memory. When a failure occurs, they can be instantly dumped to the log file.

spdlog::enable_backtrace(32); // Сохраняем последние 32 сообщения в буфер

for (int i = 0; i < 100; ++i) {
    spdlog::debug("Отладочный шаг №{}", i); // Сообщения пока не пишутся в диск
}

// При возникновении ошибки выгружаем накопленный буфер
spdlog::dump_backtrace();

Changing Log Level Without Rebuilding

Log levels can be read directly from environment variables or command-line arguments at application startup:

#include "spdlog/cfg/env.h"

int main(int argc, char* argv[]) {
    spdlog::cfg::load_env_levels();
    spdlog::info("Приложение успешно запущено");
}

Running the binary with the SPDLOG_LEVEL=debug ./my_app flag immediately activates debug message output.

Performance Benchmarks

A synchronous single-threaded logger handles about 5.7 million calls per second when working with short strings (author's tests on Intel i7-4770). With 10 competing threads, the speed stays at around 1.6 million messages per second.

In asynchronous mode with the overflow policy (overrun), the library processes over 2.6 million operations per second. These numbers are achieved through minimal memory allocations and fast string building.

Ready-Made Integrations

The library is supported on Linux, Windows, macOS, Android, and FreeBSD. The package includes ready-made sinks for integration with syslog, Windows Event Log, Android logcat, and Qt text widgets (QTextEdit). Additionally, the codebase includes a spdlog::stopwatch stopwatch for measuring execution time of code segments and a utility for outputting binary data in hex format.

spdlog solves the logging problem without unnecessary complexity. For a small utility, it's enough to copy the header files, and for a large service, the asynchronous mode, file rotation, and on-the-fly message filtering come in handy. If you need a fast and predictable tool for C++, adding spdlog to your stack should be a top priority.

Related projects