Eine frage an die Profis hier: Wie loggt man in komplexen Anwendungen richtig ?
Was für Loglevel benutzt man in welchen fällen ? An welchen Stellen setzt man logging richtig ein ? Wie verhindert man das das Programm durch logging unleserlich wird ? Wie loggt man das man Fehler erkennen, falls es beim Kunden probleme gibt ? Wie man mit Eingang (mit oder ohne Argumente) und Ausgang (mit oder ohne result) von Methoden umgeht ?
Ich frage deshalb weil ich Beruflich große Kommerziele Server Anwendungen schreibe, die auf Windows-Dienste aufbauen und etwailige selbst entwickelte DLL´s nutzen und mind 50k Zeilen Quellcode enthalten.
Solche Anwendung sauber zu logging erweisst sich immer als Schwierig, allein schon deswegen wenn andere dran arbeiten und z.b. nur mit Debug loggen, und ich somit nach Fehler riechen muss
Es geht aber nicht darum, welche Lib ich nehmen muss zum loggen. Das Thema muss man hier nicht anschneiden und darum geht es mir nicht.
Ich will nur wissen wie man richtig loggt. Das ist alles.
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Bei uns im Projekt haben wir Templates (Eclipse-Codetemplates) welche
1. bei neuerstellten Funktionen automatisch ein if (log.isTraceEnabled()) { log.enter("Funktionsname"); } Alternativ auch mit Parametern. Das sollte zumindest bei allen public methoden gemacht werden.
beim Verlassen gibt es das Gleiche mit log.exit().
Bei jeder Exception wird das Gleiche mit log.exception() gemacht.
Außerdem sollen entwickler an entscheidenden Stellen bzw. bei langen Funktionen immer mal wieder "checkpoint" trace-logs einbauen.
Somit kannst du beim Kunden im Problemfall trace-logging aktivieren und siehst wo dein Code durchläuft. Is natürlich nix für den dauerhaften produktiveinsatz.
Normalerweise läuft das Loggin bei der Entwicklung auf DEBUG und beim Kunden auf WARN.
ERRORs werden genutzt um Fachlich falsches Verhalten abzubilden bzw um zu zeigen, dass die Anwendung in einem Bereich inkonsistent werden könnte. FATAL überhaupt nur selten und dann nur um zu zeigen, dass die Technik unvorhersehbar gestört ist. Hab ich noch nie benutzt.
Hoffe das geht in die Richtung von dem was du meinst.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Das Enter/Leave prinzip kenn ich und nutze ich auch, allerdings immer alles von hand. Wüsste nicht wie ich das in C# Generalisieren könnte
Wie ist das denn mit Info Logging ? Wann benutzt man sowas ?
Wenn man Error/Warning/Fatal loggt, macht man das nur wenn eine Exception aufgetreten ist oder auch wenn man im code schon einen Fehler erkannt hatte, z.b. Daten die reinkommen passen nicht.
Wäre sowas richtig ?
Code:
/// <summary> /// Main entry point /// </summary> /// <param name="args">Command line arguments</param> /// <returns>Application result</returns> [STAThread] static int Main(string[] args) { Logger.Info(MethodInfo.GetCurrentMethod(), "Started Worker Queue V" + Assembly.GetExecutingAssembly().GetName().Version.ToString()); Console.WriteLine("eRoom Business Reports Worker Queue V" + Assembly.GetExecutingAssembly().GetName().Version.ToString());
// Prevent that this application can be started multiple times Logger.Debug(MethodInfo.GetCurrentMethod(), "Aquire application mutex state"); Mutex singleInstanceMutex = null; try { singleInstanceMutex = Mutex.OpenExisting("EBRWorkerQueue");
// application is already open Logger.Warn(MethodInfo.GetCurrentMethod(), "Application already opened, Exiting!"); return ReturnCodes.ApplicationAlreadyOpened; } catch { // Mutex dont exists, create one Logger.Debug(MethodInfo.GetCurrentMethod(), "Create new application mutex"); singleInstanceMutex = new Mutex(true, "EBRWorkerQueue"); }
// Run Queue Application int res = ReturnCodes.Success; // By default its successfully. try { // Init worker queue / eRoom application is created internally Logger.Debug(MethodInfo.GetCurrentMethod(), "Run worker queue"); using (EBRWorkerQueue workerQueue = new EBRWorkerQueue()) { // Set compability parameter workerQueue.Compability = compabilityParam;
/// <summary> /// Runs the worker queue: /// 1.) Compability process (Only if mode is enabled) /// 2.) Clear memory job list /// 3.) Add jobs to memory job list /// 4.) Process jobs (Refresh all reports) /// </summary> /// <returns>Application result code</returns> public int Run() { Logger.Enter(MethodInfo.GetCurrentMethod());
Registriert: Di Apr 29, 2008 18:56 Beiträge: 1213
Programmiersprache: Delphi/FPC
Hey,
ich mach das immer so: Ich schreib mir in der Datei, in der ich gerade arbeite ne CompilerDirektieve und wenn die definiert ist, dann schick ich den Log Test an die Konsole (oder halt an die Log-Klasse, wenn du sowas hast). So hab ich den Überblick über die Logs un kann die, die ich nich brauch abschalten...
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Richtig loggen tut man erstmal nicht auf die Console ^^, die ist einfach übelst lahm. Ich arbeite ja auch Beruflich an Server allerdings für MMO und da geht richtig was an log über die Bühne. Hierfür nutzen wir ein Netzwerk Log-Server, welcher TB an Festplattenspeicher hat, einige Kerne, 32GB Ram läuft. Da schmeist du dann per TCP deine ganzen Messages rein, 3 Level Debug,Error,Info. Error kommen von ErrorCode und von externen exception(vermeide sowas immer, wenn möglich), Info kommen von dein Programmcode, wie z.B. StartSession(.....) und Debug sind halt alle Messages in verbindung mit einem Error, also wenn du eine exception hast, wird der funktionsstack zurück gerollt und dabei kommen weitere folgefehler, dann Informationen die im catch zweig kommen, Dumps und ähnliches. Logs für große Anwendungen sind sehr CPU und Speicherlastig, daher ein externer Server, welcher z.b. durch ein apache und xml/xslt prima live beobachtet werden kann.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Also dein Codebeispiel ist schon ziemlich krass mit Log-Ausgaben durchsetzt.
Es geht m.M.n. nicht nur darum Sachen ins log zu schreiben, sondern hilfreiche Sachen ins Log zu schreiben.
Ich habe mal eine Anwendung Supporten müssen, deren Log übersäht war mit Meldungen ala "Fatal Exception - Main Queue Missing!!!!!" oder sowas in dieser Bedeutung. Als ich dann die Leute die das bis dahin Supportet hatten gefragt habe, was das sei kam als Antwort: "Ist nicht wichtig. Kommt immer." DAS darf nicht sein.
Aussagen und Loglevel sowie Informationsgehalt muss zusammen passen.
Das Tracelogging macht man nur, damit man im Worstcase mal das Tracelogging für 5Minuten anschalten kann, wo der Code die Teile durchläuft die aller Wahrscheinlichkeit nach einen Bug haben. Ansonsten braucht man das nicht. Debug-Logging ist für die Entwickler da. Error und Warning sind wichtiger. Warnings sollten immer dann geworfen werden, wenn der Code einen Sonderfall der Spezifikation durchläuft, der eigentlich nicht üblich ist. z.B. das ein Config-File fehlt und man Default-Values verwendet. Oder wenn eine Importschnittstelle gerufen wird, und sie merkt, dass keine Daten vorliegen, obwohl laut Spec immer irgendwas kommen müsste. Das sind Warnings. Errors sind Fehler wo wirklich Exceptions fliegen. Einige Exceptions werden zwar auch nur als Warning gelogt, aber das sind erstmal alles ERROR Kandidaten. Errors innerhalb von Schleifen sollten die Werte der Schleife mitloggen.
Loggen in die Konsole direkt ist eher nicht sinnvoll, da alle Logausgaben zusammen an einem Ort sein sollten.
Man sollte nicht zuviel in ein Logfile schreiben. Erstens kostet es Zeit, dann Speicherplatz und zuletzt hilft einem "Blabla-Logs" nicht beim Fehlerfinden.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Errors/Fatal werden geloggt bei jeder Exception. Spielt dabei keine rolle ob diese Unhandled oder Handled ist.
Info werden dann Informationen sein wie z.b. "Berechne etwas"...
Debug ist das dann dazwischen Enter und Leave.
Beispiel:
Code:
int Addition(int a, int b) { Log.Enter(a, b);
Log.Debug("Initialize result with zero"); int res = 0;
Log.Info(String.Format("Calculate {0} and {1}", a, b)); try { res = a+b; Log.Debug(String.Format("Calculation from {0} and {1} succeeded.", a, b)); } catch (Exception ex) { Log.Error(String.Format("Failed calculating {0} and {1}!", a, b), ex); } Log.Leave(res); return res; }
Da ist aber noch eine Frage, bzw. eine Anforderung:
Ein Kunde macht eine Migration mit einem Tool das ich entwickelt habe. Der Kunde will unbedingt im Log erkennen welches Element grad migriert wird. Info ist aber vom Loglevel her zu hoch, da der Kunde keine unnötigen Enter/Leave... sehen will.
Macht man da dann ein Neues Loglevel für den Kunden oder bleibt man beim Info ?
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Jupp Jupp Jupp
Code:
int Addition(int a, int b) { int res = 0; Log::FunctionScopeEnter FunctionScopeDebugMessage(res,a, b);//generate enter message with init values and parameter(result,parameterlist)
try { res = a+b; } catch (Exception ex) { Log.Error(String.Format("Failed calculating {0} + {1}!", a, b), ex); } return res; }//FunctionScopeDebugMessage will bedesrtoyed and print all values again with leave message
Mein Vorschlag wäre dem Kunden ein Präfix an zu bieten, also dass vor jeder Message dann der Präfix steht z.B. "MyFramework->"+"Error:"+ErrorMsg() und "CustomerApplication->"+"Error:"+ErrorMsg(); . Dies erlaubt eine einfache Log API und man kann sehr einfach zwischen eigenen Application und fremden Bibliotheks Code unterscheiden.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Enter und Leave sind eins drunter auf Trace-Level.
Infos sind genau für sowas da wie dein Kunde will.
Hmm ich glaube ich habe die ganze zeit ein wenig was falsch gemacht. Enter und Leave gibt es ja als eigentliches Loglevel nicht, zumindest nicht in log4net.
Bisher habe ich immer Debug bei Enter und Leave benutzt, das ist natürlich nicht so toll
Code:
public enum LogLevel { Off = -1, Fatal = 0, Error = 1, Warning = 2, Info = 3, Debug = 4 }
Ich müsste also, Enter und Leave über Debug setzten. Also debug auf 5 und Enter/Leave auf 4 richtig ?
Info würde man dann nur noch für Kundeninfos, bzw. Programinfos nutzen, z.b. auch Versionsnummer der Anwendung, User context, etc.
hab den Thread nur überflogen, aber zusätzlich zu dem, was Flash sagt, mag ich noch was hinzufügen wg. Loglevels:
Warning: Für alle Ausnahmen, die zwar potentiell unerwünscht sind, von denen sich das Programm aber "erholen" kann. Also alles, wo das Programm noch sinnvoll weiterlaufen kann. Error: Für alles, was wirklich "WAaaaaaaaaaahh!" ist Darunter fallen die meisten Exceptions (Nicht alle, manche sind eher Kandidaten für Warnungen) Fatal: Etwas, was das Programm gleich am Anfang schon abbricht. Wenn die VM z.B. verreckt, nicht starten kann, wenn irgendwelche Bibliotheken fehlen. Spich alles, was wirklich fatal ist für das Programm und das keine Chance hat, durch eine Fehlerbehandlung behandelt zu werden.
Das ist natürlich auch alles abhängig vom Programm, das du schreibst. Wenn du unerwartete Ausnahmen generell auch dem User anzeigt a la einem Dialog "OOoops! The following unexpected error occured: ..." ist das was anderes als wenn du z.B. eine Web-Anwendung schreibt, die jeden Fehler mit einem HTTP 500 (Internal Server Error) beantwortet und die Exception (und ihren Stacktrace freilich) in einer html-seite anzeigt...
Mal ein Beispiel an einem Klassiker: Eine FileNotFoundException wäre für mich z.B. sowohl ein Kandidat für Warning als auch für Error - eventuell auch für Fatal: Warning Beispielsweise dann, wenn eine Konfigurationsdatei nicht gefunden wurde, das Programm aber default-Werte für die Datei hat. => Normalerweise sollte die Datei da sein, die Anwendung kann sich jedoch von dem Fehlverhalten erholen, da sie Default-Werte hat
Error Wenn das Programm eine Datei auslesen soll, die nicht da ist, aber da sein muss wie z.B. Data-Files für ein Spiel (Wenn z.B. ein Level geladen werden soll, von dem Level ist weit und breit auf der Platte aber nichts zu sehen).
Fatal Wenn du z.B. eine Bibliothek laden musst, die du fürs Programm brauchst und die ist einfach nicht da. Dann ist das etwas, was deine komplette Anwendung nutzlos machst und damit ziemlich fatal
Hoffe das hilft weiter, ~ Frase
_________________ "Für kein Tier wird so viel gearbeitet wie für die Katz'."
Mitglieder in diesem Forum: 0 Mitglieder und 7 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.