>_ DevTrendspl

Język

Strona główna

Języki

Sekcje

Frontend Backend Mobilne DevOps AI / ML GameDev Blockchain Systemy wbudowane Bezpieczeństwo
C-plus-plus

Szybkie logowanie w C++ bez bólu i ciężkich zależności

Każdy, kto pisał coś bardziej złożonego niż skrypty z tutoriali w C++, stanął przed wyborem biblioteki do logowania. Standardowa std::cout jest wygodna dla prostych narzędzi konsolowych, ale szybko staje się wąskim gardłem ze względu na wolne I/O i brak wygodnego formatowania. Alternatywy takie jak Boost.Log czy log4cxx pociągają za sobą swoje zależności, długo się kompilują i przerażają złożoną konfiguracją.

W 2014 roku developer Gabriel Melman opublikował bibliotekę spdlog. Projekt szybko zyskał popularność w społeczności C++ i ma obecnie prawie 30 000 gwiazdek na GitHub.

W czym tkwi sekret popularności

Głównym powodem sukcesu biblioteki jest równowaga między wydajnością a łatwością integracji. spdlog jest dystrybuowana w formacie header-only: wystarczy skopiować katalog include do projektu, dołączyć plik nagłówkowy i zacząć pracować. Jeśli chcesz przyspieszyć kompilację aplikacji, bibliotekę można wstępnie skompilować za pomocą CMake lub zainstalować z większości popularnych menedżerów pakietów (apt, brew, vcpkg, conan).

Formatowanie ciągów znaków bazuje na bibliotece fmt, która stała się podstawą dla std::format w C++20. Eliminuje to niewygodne łańcuchy z operatorem <<. Wyrównywanie, wyjście liczb szesnastkowych i argumenty pozycyjne są zapisywane łatwo i czytelnie.

Przydatne funkcje w codziennej pracy

Kombinowanie sinków logów

W praktyce często potrzebujesz wysyłać błędy na konsolę z podświetlaniem kolorów, jednocześnie zapisując szczegółowe informacje debugowania do pliku z rotacją. W spdlog to zadanie jest rozwiązywane przez łączenie wielu sinków w jeden obiekt loggera:

#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("Сообщение попадет и в консоль, и в файл");
}

Tryb asynchroniczny dla zadań wysokonagruzonych

W wątkach roboczych serwerów sieciowych lub silników gier bezpośrednie zapisy na dysk opóźniają wykonanie kodu. spdlog może działać asynchronicznie: wątek w tle pobiera wiadomości z bufora cyklicznego i zapisuje je na dysku bez spowalniania głównych obliczeń.

#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("Запись лога не блокирует текущий поток");
}

Bufor cykliczny do śledzenia błędów

Interesującą funkcją biblioteki jest obsługa backtrace. Załóżmy, że środowisko produkcyjne ma ustawiony poziom logowania na info, a szczegółowe debugowanie jest wyłączone. Gdy aktywowane jest backtrace, biblioteka zapisuje ostatnie N wiadomości debugowania w pamięci. Gdy wystąpi awaria, można je natychmiast zrzucić do pliku logu.

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

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

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

Zmiana poziomu logowania bez przebudowy

Poziomy logowania można odczytywać bezpośrednio ze zmiennych środowiskowych lub argumentów wiersza poleceń podczas uruchamiania aplikacji:

#include "spdlog/cfg/env.h"

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

Uruchomienie pliku binarnego z flagą SPDLOG_LEVEL=debug ./my_app natychmiast aktywuje wyjście wiadomości debugowania.

Wyniki wydajnościowe

Synchroniczny logger jednowątkowy obsługuje około 5,7 miliona wywołań na sekundę przy pracy z krótkimi ciągami znaków (testy autora na Intel i7-4770). Przy 10 konkurujących wątkach prędkość utrzymuje się na poziomie około 1,6 miliona wiadomości na sekundę.

W trybie asynchronicznym z polityką przepełnienia (overflow policy) biblioteka przetwarza ponad 2,6 miliona operacji na sekundę. Te wyniki są osiągane dzięki minimalnym alokacjom pamięci i szybkiemu budowaniu ciągów znaków.

Gotowe integracje

Biblioteka jest wspierana na Linux, Windows, macOS, Android i FreeBSD. Pakiet zawiera gotowe sinki do integracji z syslog, Windows Event Log, Android logcat i widżetami tekstowymi Qt (QTextEdit). Dodatkowo w kodzie źródłowym znajduje się stoper spdlog::stopwatch do mierzenia czasu wykonania segmentów kodu oraz narzędzie do wyprowadzania danych binarnych w formacie szesnastkowym.

spdlog rozwiązuje problem logowania bez zbędnej złożoności. Dla małego narzędzia wystarczy skopiować pliki nagłówkowe, a dla dużej usługi przydatny jest tryb asynchroniczny, rotacja plików i filtrowanie wiadomości w locie. Jeśli potrzebujesz szybkiego i przewidywalnego narzędzia dla C++, dodanie spdlog do swojego stacku powinno być priorytetem.

Powiązane projekty