easylogging++
In diesem Tutorial widme ich einmal mehr dem Thema Logger und werden easylogging++ für meine Bedürfnisse konfigurieren. Ich hoffe du kannst aus dem Artikel einige hilfreiche Tipps für deinen Logger ziehen. Dieser Beitrag ist dabei die Fortsetzung meines Professionelles Loggen in C++ Artikels.
easylogging++
Der Single Header Logger easylogging++ war recht einfach in meine Logging Schnittstelle einzubauen. In der Defaultkonfiguration wird auf die Standardausgabe geloggt. Das ist zwar im Debugmodus nett, spätestens im produktiven Umfeld würde man dann doch eine *.log Datei bevorzugen. Will man das haben, dann muss man easylogging++ konfigurieren. Die Bibliothek kann laut Dokumentation wie folgt konfiguriert werden:
- configuration file
- el::Configurations class
- inline configuration
Da ich an einem Projekt arbeite, dass eines Tages einen produktiven Status erreichen soll entscheide ich mich gleich für die Option mit der Konfigurationsdatei. So kann ich ohne Source Code Änderung das Logging der *.exe einstellen. Idealerweise erstellt man unterschiedliche Konfigurationen für Debug und Release.
Konfiguration
Ich konfiguriere meinen Logger über eine Datei, die ich später dem fertigen Programm mit geben. Eine einfache Konfiguration sieht dabei so aus:
## Configuration file for easylogging++ * GLOBAL: FORMAT = "%datetime %msg" FILENAME = "simulation.log" ENABLED = true TO_FILE = true TO_STANDARD_OUTPUT = false SUBSECOND_PRECISION = 2 PERFORMANCE_TRACKING = false MAX_LOG_FILE_SIZE = 2097152 ## 2MB - Comment starts with two hashes (##) LOG_FLUSH_THRESHOLD = 100 ## Flush after every 100 logs * INFO: FORMAT = "%datetime %msg" TO_STANDARD_OUTPUT = true PERFORMANCE_TRACKING = true
In der Sektion GLOBAL werden alle allgemein gültigen Konfigurationen getätigt. Danach setze ich für jeden Log Level Einstellungen die davon abweichen. So wird beispielsweise das Format der Meldungen gesetzt, die Datei in der diese geschrieben werden und ob das Logging aktiv ist oder nicht. Über diese Konfigurationsdatei kann man somit einzelne Logstufen ein- und ausschalten. Das ist super Hilfreich um gezielter zu loggen, im Programm macht es also Sinn, dass man so viel wie möglich an Logs erfasst (natürlich nur, wenn die nicht alle den selbe Logstufe haben).
Nähere Details zu den einzelnen Einstellungen sind der Dokumentation von easylogging++ zu entnehmen. Meiner Meinung nach ist das sehr gut beschrieben und es gibt genügend Möglichkeiten das Logging anzupassen.
Logging Factory
Ich habe meinen Logger über das Factory Design Pattern ins Programm eingebaut. Damit bin ich nun flexibel genug um bei Bedarf die Loggingklasse zu verändern. Diese Factory erweitert das zuletzt vorgestellte Logging:
#include "LoggingFacility.h" #include "StandardOutputLogger.h" #include "FilesystemLogger.h" class LoggerFactory { public: static Logger create() { // read config file from filesystem std::string filename = "logger.conf"; std::ifstream filestream(filename); // if configuration file exists - use FilesystemLogger if (filestream.good()) { el::Configurations conf(filename); el::Loggers::reconfigureAllLoggers(conf); return std::make_shared<FilesystemLogger>(); } // if not available use StandardOutputLogger return std::make_shared<StandardOutputLogger>(); } };
Die Factory hat zwei Logger zur Verfügung. Das ist die Standard Ausgabe und eine Log Datei im Filesystem. Je nachdem ob eine Konfiguration für das verwendete easylogging++ existiert oder nicht wird der entsprechende Logger erzeugt.
Im Programm erstelle ich den Logger über folgenden Aufruf:
Logger logger = LoggerFactory::create();
Dabei abstrahiert der Logger jegliche Information über Funktionalität und aufbau. Die aufrufende Klasse muss also nichts darüber wissen wie Logging funktioniert. Dieses Information Hiding ist ein wichtiger Grundsatz moderner objektorientierter Programmierung.
Fazit
Ich bin mit easylogging++ sehr glücklich. Es war sehr einfach in das bestehende Projekt dank professioneller Logging Schnittstelle einzubauen. Die Konfiguration ist auf Dateiebene sinnvoll um auch in der Produktivumgebung die nötige Flexibilität zu haben. Wichtig ist jetzt nur noch, dass man die verfügbaren Logstufen klug einsetzt und im Programm an den richtigen stellen Ausgaben erzeugt. Einem einfachen Debuggen steht nun nichts mehr im Weg.