Gibs Sprachfeatures fuers Logging?
-
Hallo
Logging in Programmen ist ja sehr wichtig. Ich selbst benutze es sehr oft und es hilft Fehler zu finden, die selten auftreten, weil man durch das Logging alles nachvollziehen kann. Allerdings ist es in mir bekannte Sprachen sehr aufwaendig und sehr haesslich Logging zu betreiben.
In Java sieht das z.B. so aus:static { logger = Logger.createLogger(MeineKlasse.class.getName()); } // ... if ( logger.isLogable(Level.FINER) ) logger.finer("blub");In anderen Sprachen sicher aehnlich. Man prueft ob ein Logging aktiviert ist und dann muss man etwas in den Logger schreiben.
Dies ergibt aber einen etwas unleserlichen Code, weil man staendig durch diese Ifs genervt ist. Man kann sich dann einfach nicht auf den wesentlichen Code konzentrieren.Gibts keine Alternativen, die mehr im Hindergrund sind?
Ich koennte mir sowas vorstellen:
@logger(Level.FINER, "blub")Der Vorteil ist das der Code-Hervorheber alle Annotationen Grau (also andere Farbe als der "echte" Code) darstellen kann, ausserdem es ist weniger Text. Ebenso koennte man alle Zeilen mit @logger einfach ausblenden lassen.
Gibts etwas aenliches oder besseres, vllt. auch in anderen Sprachen?
-
In C oder C++ könnte ich mir vorstellen, mit dem Präprozessor etwas ähnliches bereitzustellen.
-
CStoll schrieb:
In C oder C++ könnte ich mir vorstellen, mit dem Präprozessor etwas ähnliches bereitzustellen.
In den cxxtools-Bibliotheken von tntnet wird das genau so gemacht. Gefällt mir persönlich sehr gut, vor allem da bei deaktiviertem Logging die Aufrufe komplett eliminiert werden. Einziger Nachteil: Das Verfahren benutzt halt Makros.
// Einmalig, Z.B. in main() log_init(); // Global je Quelldatei log_define( "main.cpp" ); // Anwendung in Funktionen log_debug( "Ich bearbeite gerade " << m_data << " mit " << m_tool );
-
Ja, an so etwas habe ich gedacht mit dem Präprozessor
(btw, was ist der Nachteil darin, Makros einzusetzen? Wenn angebracht, sind die durchaus nützlich)
-
CStoll schrieb:
(btw, was ist der Nachteil darin, Makros einzusetzen? Wenn angebracht, sind die durchaus nützlich)
Der Standard-Nachteil: Wenn jemand anderes oder ich selbst irgendwo einen Bezeichner "log_define" in einem Modul deklariert, welches ich zufällig nach cxxtools/log.h einbinde, knallt es.
Deshalb benutze ich (wenn denn nötig) Makronamen wie KUERZEL_PROJEKTNAME_MAKRONAME und undefiniere sie so früh wie möglich wieder

-
LordJaxom schrieb:
CStoll schrieb:
(btw, was ist der Nachteil darin, Makros einzusetzen? Wenn angebracht, sind die durchaus nützlich)
Der Standard-Nachteil: Wenn jemand anderes oder ich selbst irgendwo einen Bezeichner "log_define" in einem Modul deklariert, welches ich zufällig nach cxxtools/log.h einbinde, knallt es.
Deshalb benutze ich (wenn denn nötig) Makronamen wie KUERZEL_PROJEKTNAME_MAKRONAME und undefiniere sie so früh wie möglich wieder

Wäre ja eine Idee. Ich wollte halt was ganz kurzes, welches aus dem Code nicht so stark heraus sticht. Das Logging soll ja eher so nebenher laufen. Aber wie wäre es mit:
CXXTOOLS_LOG_INIT, CXXTOOLS_LOG_DEFINE, CXXTOOLS_LOG_DEBUG, CXXTOOLS_LOG_INFO,...?
GROSSBUCHSTABEN werden halt üblicherweise zum schreien verwendet und sehen daher in meinen Augen recht aggressiv aus. Wäre auch relativ aufwändig, das jetzt noch zu ändern.Gruß
Tntnet
-
@tntnet
man kann ja zur Lesbarkeit immer noch ein#define log_init CXXTOOLS_LOG_INITmachen, wenn man keinen Namenskonflikt hat
-
Ich dachte eher an echte Sprachfeatures in einer Sprache fuer das Logging. Es wundert mich aber wieso es nicht in modernen Sprachen implementiert. Scheint Logging doch nicht so wichtig zu sein?
-
außer in der debugphase kaum. klar, wenn im produktiveinsatz mal ganz seltsame probleme auftauchen, wünscht man sich vielleicht nen detailiertes logging, aber normalerweise braucht man sowas nicht.
und in den bereichen, in denen logging wichtig ist, ist der code meist eh schon sowas von dermaßen hässlich und unportabel, dass es auf nen reingeklebtes logging auch nicht mehr ankommt

-
Das Filtern ob Logging aktiviert ist kann ja im Logger passieren. Mir persönlcih gefällt Log4j unter Java sehr gut.
MfG SideWinder
-
Logging kostet Performance. Wie hier gesagt wurde, ist im Debugging oder Testing ein Logging sehr nützlich. Bei Desktop-Anwendungen kann man vielleicht noch eine "erste Zeit lang" Logging betreiben. Auf einem Server der mit Anfragen befeuert wird und maximale Leistung bringen soll, ist es im produktiven Einsatz nicht hilfreich.
Da finde ich dann das Log4J schon nicht schlecht, wo ich Debug-Levels habe. D.h. ich kann im produktiven Einsatz den Level so ändern, das nur noch das wichtigste aufgezeichnet wird... z.B. Fehlermeldungen.
Ich mache das übrigens so:
#ifdef _DEBUG std::clog << "Meine Debug-Info"; #endif // _DEBUGSehr spartanisch, aber nützlich.

-
Logging kostet Performance. Wie hier gesagt wurde, ist im Debugging oder Testing ein Logging sehr nützlich. Bei Desktop-Anwendungen kann man vielleicht noch eine "erste Zeit lang" Logging betreiben. Auf einem Server der mit Anfragen befeuert wird und maximale Leistung bringen soll, ist es im produktiven Einsatz nicht hilfreich.
Wo ist das Problem, beim mySQL Server kann man ebenfals Loging ein und ausschalten wie man lustig ist. Sowas koennte eine VM ganz gut optimieren, indem es einfach das Loging ganz entfernt, wenn es feststeht das loging nie benutzt wird.
In Java verwende ich die Loging Klasse von der JRE, gefaellt mir ganz gut, nur wuerde es wirklich besser sein sowas in die Sprache zu integrieren. Zum einen koennte man dann den Preprozessor weglassen (und damit sich etliche Nachteile ersparen) und der Compiler koennte einen dabei auch unterstuetzen. Auch koennte der Compiler Loging automatisieren (zum Beispiel bei Exceptions, bei ein/austritten aus/in Methoden oder beim Speichermanagment).
Ebenso ist ein Loging beim fertigen Produkt nicht zu unterschaetzen. Wenn was beim Kunden schiefgeht, kann man gleich nachvollziehen was er gemacht hat und was dabei schiefgegangen ist. Klar macht man bei einem zeitkritischen Code kein Loging, da aber eh 90% des Codes fuer die Perfomance irrelevant ist, macht es nichts aus dort ein "if" zusaetzlich zu haben.
-
Sehe ich genauso, 90% der Zeit warten normale Desktop-Programme auf Input vom Benutzer, was spricht da gegen ein gutes Logging.
Bei uns wird bei Auftreten einer Exception dem Kunden automatisch offeriert das Log-File vollautomatisch an uns zu versenden. Ein kleiner Kommentar noch in eine TextArea und wir haben bereits die Möglichkeit den Fehler nachzuvollziehen und zu fixen. Gut, wir haben es in erster Linie auch mit Kleinkunden zu tun und der gute Kundenkontakt ist unser großer Vorteil. Aber auch große Firmen können mit gutem Logging viele Debug-Stunden einsparen. Kommt doch etwas mehr als "es ist abgestürzt" als Feedback.
MfG SideWinder
-
Aber warum sollte man fürs Loggen extra solche Sonderzeichen in die Sprache einbauen?
log(VERBOSE, "Hallo") tuts auch überall. Es wäre höchstens schön wenn in der Standard-Sprachbibliothek solche Funktionen vorhanden wären, dann könnten Editoren die Zeile auch hervorheben, einklappen, oder sonstwas.
-
DEvent schrieb:
Ich dachte eher an echte Sprachfeatures in einer Sprache fuer das Logging. Es wundert mich aber wieso es nicht in modernen Sprachen implementiert. Scheint Logging doch nicht so wichtig zu sein?
Warum sollte man daraus ein Sprachfeature machen? Das passt doch gut in eine Funktion oder in ein Macro.
-
log(VERBOSE, "Hallo")
aber wenn man in dem string noch variablen hat und die vorher per stringstream zusammen bauen muss wirds eckelig
-
verbose schrieb:
log(VERBOSE, "Hallo")
aber wenn man in dem string noch variablen hat und die vorher per stringstream zusammen bauen muss wirds eckelig
Deswegen nimmt man ja idr. ein Macro oder printf-Style-Formatierung.
-
das is ja eher ein allgemeines Problem, wie man die Ausgaben formatiert.
-
Hier wird auf die zahlreichen Nachteilen des Präprozessors hingewiesen. Sicher hat der Präprozessor seine Nachteile, aber gerade beim Logging kann er die angesprochenen hässlichen Dinge verbergen ohne es gleich in die Sprache zu integrieren. Schauen wir uns doch mal so ein mögliches Log-statement mit allen Schmakazien mal an. Ich habe das Beispiel des original Posters mal ein wenig ausgebaut:
inline static Logger& getLogger() { static Logger logger = 0; if (logger == 0) logger = Logger.createLogger("project1.modul17.myclass"); return logger; } // ... if ( logger.getLevel() > Level.DEBUG ) { logger.getOStream() << "der Wert ist " << a; }logger.getLevel() sei eine inline-Methode, so daß sich die Entscheidung, ob etwas ausgegeben wird auf ein Vergleich zweier ganzer Zahlen reduziert. Das geschieht zur Laufzeit, so daß das Logging über eine Konfigurationsdatei gesteuert werden kann.
Das angesprochene Problem mit der Formatierung habe ich hier durch einen std::ostream gelöst. Das ist sehr billig, da das nur ausgeführt wird, wenn logging auch aktiviert ist.
Das ganze ist natürlich sehr unschön. Das gebe ich zu. Aber glücklicherweise bietet uns diese grossartige Programmiersprache C++ doch eine Lösung dafür: den Prärpozessor. Wir definieren also ein paar Makros:#define log_define(clazz) inline static Logger& getLogger() \ { static Logger logger = 0; \ if (logger == 0) \ logger = Logger.createLogger(clazz); \ return logger; } #define log_debug(expr) if ( logger.getLevel() > Level.DEBUG ) { \ logger.getOStream() << expr; } #define log_info(expr) if ( logger.getLevel() > Level.INFO ) { \ logger.getOStream() << expr; } #define log_warn(expr) if ( logger.getLevel() > Level.WARN ) { \ logger.getOStream() << expr; } #define log_error(expr) if ( logger.getLevel() > Level.ERROR ) { \ logger.getOStream() << expr; }Das Codebeispiel reduziert sich damit auf:
log_define("project1.modul17.myclass") // ... log_debug("der Wert ist " << a);Schön kompakt und lesbar. Ein Programm mit deaktivierten Logging läuft fast genauso schnell, wie ohne. Und wenn nun wirklich das letzte herausgeholt werden muß, dann werden die Markos einfach durch leere Definitionen ersetzt. Also ich finde den Präprozessor für solche Anwendungen durchaus empfehlenswert.
Das oben beschriebene hat noch ein paar kleine Probleme, die aber lösbar sind. Ich habe das ganze, wie LordJaxom bereits erwähnt hat in meiner Open-Source-Bibliothek cxxtools veröffentlicht.Gruß
Tntnet
-
Um nochmal auf Deine erste Antwort zurückzukommen, natürlich sollst Du nicht nachträglich irgendwas ändern. Ich benutze das System ja selbst. Ich wollte nur auf die möglichen Nachteile des Präprozessors hinweisen.
Da ich außerhalb des Logging äußerst selten Bezeichner mit Namen log_* wähle, ist es hier in Realworld-Code auch noch nie zu Konflikten gekommen.
Falls es wirklich mal zu Konflikten kommen sollte, könnte man allerdings immernoch den Vorschlag aufgreifen, die Makros als CXXTOOLS_LOG_* zu definieren und ein zusätzliches Include bereitzustellen, welches die alten Makros bei Bedarf hinzudefiniert.