Schnelles C++-Logging ohne Aufwand und schwere Abhängigkeiten
Jeder, der in C++ etwas Komplexeres als Tutorial-Skripte geschrieben hat, stand vor der Wahl einer Logging-Bibliothek. Der Standard std::cout ist für einfache Konsolen-Tools praktisch, wird aber schnell zum Flaschenhals aufgrund langsamer I/O und fehlender komfortabler Formatierung. Alternativen wie Boost.Log oder log4cxx bringen ihre Abhängigkeiten mit, brauchen lange zum Kompilieren und sind mit ihrer komplexen Konfiguration einschüchternd.
Im Jahr 2014 veröffentlichte der Entwickler Gabriel Melman die spdlog-Bibliothek. Das Projekt erlangte schnell Popularität in der C++-Gemeinschaft und hat mittlerweile fast 30.000 Sterne auf GitHub.
Was macht es so beliebt
Der Hauptgrund für den Erfolg der Bibliothek ist das Gleichgewicht zwischen Performance und einfacher Integration. spdlog wird im Header-Only-Format verteilt: Sie kopieren einfach das include-Verzeichnis in Ihr Projekt, inkludieren die Header-Datei und beginnen zu arbeiten. Wenn Sie den Build Ihrer Anwendung beschleunigen möchten, kann die Bibliothek über CMake vorkompiliert oder über die gängigsten Paketmanager (apt, brew, vcpkg, conan) installiert werden.
Die String-Formatierung basiert auf der fmt-Bibliothek, die zur Grundlage für std::format in C++20 wurde. Das erspart umständliche Verkettungen mit dem <<-Operator. Ausrichtung, hexadezimale Zahlen-Ausgabe und Positionsargumente werden einfach und übersichtlich geschrieben.
Nützliche Funktionen für die tägliche Entwicklung
Log-Sinks kombinieren
In der Praxis müssen Fehler oft mit Farbhervorhebung an die Konsole gesendet werden, während detaillierte Debug-Informationen in eine rotierende Datei geschrieben werden. In spdlog wird diese Aufgabe gelöst, indem mehrere Sinks zu einem einzigen Logger-Objekt kombiniert werden:
#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("Сообщение попадет и в консоль, и в файл");
}
Asynchroner Modus für hochgelastete Aufgaben
In Worker-Threads von Netzwerk-Servern oder Game-Engines verzögern direkte Schreibvorgänge auf die Festplatte die Code-Ausführung. spdlog kann asynchron arbeiten: Ein Hintergrund-Thread entnimmt Nachrichten aus einem Ring-Buffer und schreibt sie auf die Festplatte, ohne die Hauptberechnungen zu verlangsamen.
#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 für Fehlerverfolgung
Eine interessante Funktion der Bibliothek ist die Unterstützung für backtrace. Angenommen, die Produktionsumgebung hat den Log-Level auf info gesetzt und detailliertes Debugging ist deaktiviert. Wenn backtrace aktiviert wird, speichert die Bibliothek die letzten N Debug-Nachrichten im Speicher. Wenn ein Fehler auftritt, können sie sofort in die Log-Datei geschrieben werden.
spdlog::enable_backtrace(32); // Сохраняем последние 32 сообщения в буфер
for (int i = 0; i < 100; ++i) {
spdlog::debug("Отладочный шаг №{}", i); // Сообщения пока не пишутся в диск
}
// При возникновении ошибки выгружаем накопленный буфер
spdlog::dump_backtrace();
Log-Level ändern ohne Neukompilierung
Log-Level können direkt aus Umgebungsvariablen oder Kommandozeilen-Argumenten beim Anwendungsstart gelesen werden:
#include "spdlog/cfg/env.h"
int main(int argc, char* argv[]) {
spdlog::cfg::load_env_levels();
spdlog::info("Приложение успешно запущено");
}
Das Ausführen der Binary mit dem SPDLOG_LEVEL=debug ./my_app-Flag aktiviert sofort die Debug-Nachrichtenausgabe.
Performance-Benchmarks
Ein synchroner Single-Thread-Logger bewältigt etwa 5,7 Millionen Aufrufe pro Sekunde bei kurzen Strings (Tests des Autors auf Intel i7-4770). Mit 10 konkurrierenden Threads bleibt die Geschwindigkeit bei rund 1,6 Millionen Nachrichten pro Sekunde.
Im asynchronen Modus mit der Overflow-Policy (overrun) verarbeitet die Bibliothek über 2,6 Millionen Operationen pro Sekunde. Diese Zahlen werden durch minimale Speicherallokationen und schnellen String-Aufbau erreicht.
Fertige Integrationen
Die Bibliothek wird auf Linux, Windows, macOS, Android und FreeBSD unterstützt. Das Paket enthält fertige Sinks für die Integration mit syslog, Windows Event Log, Android logcat und Qt-Text-Widgets (QTextEdit). Zusätzlich enthält der Code eine spdlog::stopwatch-Stoppuhr zur Messung der Ausführungszeit von Code-Segmenten und ein Utility zur Ausgabe von Binärdaten im Hex-Format.
spdlog löst das Logging-Problem ohne unnötige Komplexität. Für ein kleines Tool reicht es, die Header-Dateien zu kopieren, und für einen großen Service kommen der asynchrone Modus, die Dateirotation und das Filtern von Nachrichten in Echtzeit gelegen. Wenn Sie ein schnelles und vorhersehbares Tool für C++ benötigen, sollte das Hinzufügen von spdlog zu Ihrem Stack eine Top-Priorität sein.
Ähnliche Projekte