Logging in C++-Anwendungen: Best Practices
Logging ist ein grundlegender Bestandteil der Entwicklung jeder ernsthaften C++-Software. Ohne ein zuverlässiges Logging-System werden das Debuggen komplexer Fehler, die Leistungsüberwachung und die Sicherheitsprüfung praktisch unmöglich. In diesem Artikel behandeln wir die Best Practices für Logging in C++, von der Bibliotheksauswahl bis hin zu fortgeschrittenen Techniken für Formatierung und Dateirotation.
1. Auswahl einer Logging-Bibliothek
In C++ gibt es mehrere ausgereifte Bibliotheken für das Logging. Der Verzicht auf eine eigene Implementierung zugunsten einer fertigen Lösung spart Zeit und reduziert die Anzahl der Fehler.
- spdlog — die beliebteste Bibliothek. Sie zeichnet sich durch hohe Leistung (header-only, asynchroner Modus), einfache API und Unterstützung vieler Formate (JSON, plain text) aus.
- Boost.Log — eine leistungsstarke, aber schwergewichtigere Bibliothek aus dem Boost-Umfeld. Ideal für Enterprise-Projekte, die komplexe Filterung und flexible Backend-Konfiguration erfordern.
- Google Logging (glog) — eine einfache Bibliothek von Google. Gut geeignet für kleine Projekte, aber weniger flexibel.
Beispiel für die Initialisierung von spdlog:
#include <spdlog/spdlog.h>#include <spdlog/sinks/basic_file_sink.h>
int main() { // Erstelle einen Logger, der in die Datei "logs/app.log" schreibt auto logger = spdlog::basic_logger_mt("file_logger", "logs/app.log"); // Setze die Logging-Stufe logger->set_level(spdlog::level::debug); // Protokolliere eine Nachricht logger->info("Anwendung gestartet"); return 0;}2. Logging-Stufen und Filterung
Die Verwendung von Logging-Stufen (trace, debug, info, warning, error, critical) ist der De-facto-Standard. Sie ermöglichen eine flexible Anpassung des Ausgabeumfangs je nach Umgebung (Entwicklung, Test, Produktion).
Empfehlungen zu den Stufen:
- Trace/Debug — nur für die Entwicklung. Detaillierte Informationen über Ausführungsabläufe, Variablenwerte.
- Info — Standardereignisse: Start/Stopp eines Dienstes, erfolgreiche Operationen.
- Warning — ungewöhnliche Situationen, die nicht zu einem Fehler führen (z. B. Verwendung einer veralteten API).
- Error — Fehler, die Aufmerksamkeit erfordern, aber nicht fatal sind.
- Critical — fatale Fehler, nach denen die Anwendung nicht weiterarbeiten kann.
Beispiel für die Filterkonfiguration in spdlog:
// In der Produktion schreiben wir nur warning und höherauto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();console_sink->set_level(spdlog::level::warn);
// In die Datei schreiben wir alles, einschließlich debugauto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/debug.log");file_sink->set_level(spdlog::level::debug);
auto logger = std::make_shared<spdlog::logger>("multi_sink", spdlog::sinks_init_list({console_sink, file_sink}));
spdlog::register_logger(logger);3. Leistung und asynchrones Logging
Synchrones Logging kann besonders in hochbelasteten Systemen zum Leistungsengpass werden. Jeder Aufruf von logger->info(...) blockiert den Thread, bis der Schreibvorgang auf die Festplatte abgeschlossen ist.
Best Practices für die Leistung:
- Asynchrones Logging — verwenden Sie einen separaten Thread zum Schreiben der Logs. spdlog unterstützt dies von Haus aus.
- Vermeiden Sie teure Operationen in Log-Zeilen — rufen Sie keine schweren Funktionen innerhalb des Logging-Makros auf. Beispielsweise führt
logger->info("Result: {}", computeExpensiveValue());dazu, dasscomputeExpensiveValue()auch dann ausgeführt wird, wenn die Log-Stufe nicht