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