Logging C++ veloce senza dolori e dipendenze pesanti
Chiunque abbia scritto qualcosa di più complesso degli script tutorial in C++ si è trovato davanti alla scelta di una libreria di logging. La std::cout standard è comoda per semplici utility console, ma diventa rapidamente un collo di bottiglia a causa dell'I/O lento e della mancanza di formattazione comoda. Alternative come Boost.Log o log4cxx portano con sé le loro dipendenze, richiedono molto tempo per essere compilate e sono scoraggianti per la loro configurazione complessa.
Nel 2014, lo sviluppatore Gabriel Melman ha pubblicato la libreria spdlog. Il progetto ha rapidamente guadagnato popolarità nella comunità C++ e ora ha quasi 30.000 stelle su GitHub.
Cosa la rende popolare
Il motivo principale del successo della libreria è l'equilibrio tra prestazioni e facilità di integrazione. spdlog è distribuita in formato header-only: basta copiare la directory include nel proprio progetto, includere il file header e iniziare a lavorare. Se hai bisogno di velocizzare la compilazione della tua applicazione, la libreria può essere precompilata tramite CMake o installata dai gestori di pacchetti più diffusi (apt, brew, vcpkg, conan).
La formattazione delle stringhe si basa sulla libreria fmt, che è diventata la base per std::format in C++20. Questo elimina le noiose catene con l'operatore <<. L'allineamento, l'output di numeri esadecimali e gli argomenti posizionali sono scritti in modo facile e chiaro.
Funzionalità utili per lo sviluppo quotidiano
Combinare i sink di log
In pratica, spesso è necessario inviare gli errori alla console con l'evidenziazione a colori, mentre si scrivono informazioni di debug dettagliate in un file con rotazione. In spdlog, questo compito viene risolto combinando più sink in un singolo oggetto logger:
#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("Сообщение попадет и в консоль, и в файл");
}
Modalità asincrona per attività con carico elevato
Nei thread worker dei server di rete o dei motori di gioco, le scritture dirette su disco ritardano l'esecuzione del codice. spdlog può funzionare in modo asincrono: un thread in background preleva i messaggi da un ring buffer e li scarica su disco senza rallentare i calcoli principali.
#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 per la tracciatura degli errori
Una caratteristica interessante della libreria è il supporto per backtrace. Supponiamo che nell'ambiente di produzione il livello di log sia impostato su info e il debug dettagliato sia disabilitato. Quando backtrace viene attivato, la libreria salva gli ultimi N messaggi di debug in memoria. Quando si verifica un errore, possono essere immediatamente scaricati nel file di log.
spdlog::enable_backtrace(32); // Сохраняем последние 32 сообщения в буфер
for (int i = 0; i < 100; ++i) {
spdlog::debug("Отладочный шаг №{}", i); // Сообщения пока не пишутся в диск
}
// При возникновении ошибки выгружаем накопленный буфер
spdlog::dump_backtrace();
Cambiare il livello di log senza ricompilare
I livelli di log possono essere letti direttamente dalle variabili d'ambiente o dagli argomenti della riga di comando all'avvio dell'applicazione:
#include "spdlog/cfg/env.h"
int main(int argc, char* argv[]) {
spdlog::cfg::load_env_levels();
spdlog::info("Приложение успешно запущено");
}
Eseguendo il binario con il flag SPDLOG_LEVEL=debug ./my_app si attiva immediatamente l'output dei messaggi di debug.
Benchmark delle prestazioni
Un logger sincrono single-threaded gestisce circa 5,7 milioni di chiamate al secondo quando lavora con stringhe corte (test dell'autore su Intel i7-4770). Con 10 thread in competizione, la velocità si mantiene intorno a 1,6 milioni di messaggi al secondo.
In modalità asincrona con la policy di overflow (overrun), la libreria elabora oltre 2,6 milioni di operazioni al secondo. Questi numeri sono raggiunti attraverso allocazioni di memoria minime e costruzione veloce delle stringhe.
Integrazioni pronte all'uso
La libreria è supportata su Linux, Windows, macOS, Android e FreeBSD. Il pacchetto include sink pronti all'uso per l'integrazione con syslog, Windows Event Log, Android logcat e widget di testo Qt (QTextEdit). Inoltre, il codebase include un cronometro spdlog::stopwatch per misurare il tempo di esecuzione dei segmenti di codice e un'utilità per l'output di dati binari in formato esadecimale.
spdlog risolve il problema del logging senza complessità inutili. Per una piccola utility, è sufficiente copiare i file header, e per un servizio grande, la modalità asincrona, la rotazione dei file e il filtraggio dei messaggi al volo tornano utili. Se hai bisogno di uno strumento veloce e prevedibile per C++, aggiungere spdlog al tuo stack dovrebbe essere una priorità assoluta.
Progetti correlati