Folgende Situation sei gegeben: In setup_init() wird is_valid_obj()
aufgerufen und in is_valid_obj() wird is_command() aufgerufen.
Wenn is_valid_obj() oder is_command() false zurückgeben, soll jeweils
eine Fehlermeldung ausgegeben werden. Die Fehlermeldung kann
Informationen über die fehlerhaften Variablen/Objekte enthalten.
Die Frage lautet: Wo gehören diese Fehlermeldungen hin? Sollen die
Fehler direkt in is_valid_obj() und is_command() ausgegeben werden oder
in setup_init()?
Generell gilt also die Frage, sollen Fehler am Ort ausgegeben werden, wo
auf die möglichen Fehler geprüft wird, oder am Ort wo die möglichen
fehlerhaften Werte/Objekte benutzt werden?
Häufig wird man da, wo der Fehler erkannt wird, gar nicht wissen, wie
eine Fehlermeldung ausgegeben werden soll. Also kann sie dort nur
sinnvoll eingetütet und nach oben weitergereicht werden.
Irgendwo weiter oben kann dann jemand vernünftig mit dem Fehler umgehen
und darauf reagieren, ggf. weiter nach oben durchreichen.
Wieso fällt mir da jetzt wieder C++ mit Ausnahmen ein?
Ausgegeben ?
Da, wo sie jemand sieht, Konsole, Errorlog, Schnittstelle.
Die Frage ist eher, wo die Klartexte im Programm stehen sollen:
Im Programm an der Stelle an der sie auftreten, an der sie ausgegeben
werden, oder in einer Resource.
Wegen Übersetzung sollte der Text in einer Resource stehen, also
irgendwo zusammengefasst und je nach Sprache umschaltbar. Problematisch
ist dabei die Ergänzung um Parameter, die liegen ja meist nur an der
Stelle vor, an der der Fehler entdeckt wird, wenn überhaupt.
Interessanter sind Betrachtungen: was passiert mit einem Errorlog wenn
ein Fehler dauernd auftritt, läuft dann die Festplatte voll, stockt der
Rechner wegen Überlastung weil der Fehler sagt dass die Festplatte voll
ist ? Daher am besten Fehler nur ein Mal speichern und auf Abfrage
liefern aber nichts vollmüllen. Was passiert wenn die Resource eine
extra Datei ist und nicht hefunden wird.
CleanCoder schrieb:> Generell gilt also die Frage, sollen Fehler am Ort ausgegeben werden, wo> auf die möglichen Fehler geprüft wird, oder am Ort wo die möglichen> fehlerhaften Werte/Objekte benutzt werden?
Darauf gibt es keine generelle Antwort. Im Gegenteil: Es spaltet die
Entwickler unterschiedlicher Horizonte unversöhnlich und ist eine der
wirklich kritischen Designebenen.
Beispiel 1: Bei PC-Anwendungen kann problemlos eine Fehlermeldung "kein
Speicherplatz" bis oben blockierend hochpoppen. Er kann dann welchen
Schaffen (Dateien löschen) oder das ganze abbrechen. Im Embedded-Bereich
geht das meist nicht.
Beispiel 2: Wenn eine erste Aktion nicht klappt, liegt es am Aufrufer
(der Funktion), zu entscheiden, ob er dann mit einer Alternative
weitermachen kann oder nicht. Ob und wer nun was meldet, hängt auch
davon ab, wie oft der Fehler im Fehlerfall auftritt. Meist ist es
wichtig, jeden Fehler zu loggen, aber nicht jeden Fehler beliebig oft.
Wenn Beispielsweise ein Aktor/Sensor X nicht da ist, sollte das nicht
unbedingt jede ms geloggt werden, nur weil er jede ms ausgelesen wird.
Da kann es z.B. sinn machen, nur Änderungen zu loggen.
Mal so, mal so.
Für das eine Programm ist die direkte Fehlermeldung am besten, für das
andere ist es besser, wenn der Aufrufer über die Behandlung entscheidet.
Es kommt alles auf die Art des Programmes an.
Manchmal gebe ich eine Klartext Fehlermeldung über einen
Referenzparameter zurück. Dann kann der Aufrufer entscheiden, wie er
damit weiter macht. Ausgeben, oder selber an seinen Aufrufer weiter
höher zurück geben. usw
Voodoopriester schrieb:> Jede grösser Software schreibt ein Log (oder mehrere), dafür gibts ne> API oder was selber gestricktes.
Solange Herr CleanCoder nichts zu seiner Umgebung sagt:
MaWin schrieb:> Ausgegeben ?> Da, wo sie jemand sieht, Konsole, Errorlog, Schnittstelle.
Wenn der Kram auf einen µC läuft, wird das eng mit Logfile.
Als nächstes muß der gwünschte Debugging-Prozess beschrieben werden.
Auf einem µC springe ich zum Teil eine eigene Routine an und übergebe
der nur eine Nummer, um zu sehen, an welcher Stelle er ausgestiegen ist.
Da kommt dann auf der seriellen z.B. "Scheiße passiert 3" und im
Quellcode kann ich nachsehen, dass der Abflug #3 zur Spannungsmessung
gehört.
Dann baue ich mir temporäre Ausgaben in diese Routine und suche den
Fehler. Für dauerhaft komfortablere Fehlerausgaben fehlt oftmals der
Speicherplatz.
Wenn es tatsächlich eine PC-Anwendung mit Logdatei(en) ist:
MaWin schrieb:> was passiert mit einem Errorlog wenn> ein Fehler dauernd auftritt, läuft dann die Festplatte voll,
Darüber musste ich mich einige Male mit Softwerkern prügeln, weil sie
genau das nicht beachtet hatten und auch nicht begreifen wollten - nach
wenigen Stunden fliegt Windows ab "Platte voll".
Dann antwortet der Dussel, die Fehlerausgabe sei eine Java-Klasse, auf
die er keinen Einfluß nehmen könne ...
Die Frage stellt sich mir unabhängig von der Plattform. Gemeint ist wo
im Quelltext der Fehler in die Konsole oder in die Logdatei geschrieben
werden soll.
Konkret habe ich es mit dem Browser mit JavaScript und Rust in der
Desktop Konsole zu tun.
Vor allem bei JavaScript bin ich total verloren. Arbeite ich mit bool
als Rückgabewerten? Doch die böse null nutzen? Oder bastel ich meine
eigenen Fehlerklassen?
Habe den Eindruck dass ich durch Rust erst merke wie schlimm JavaScript
eigentlich ist :D
Bin im Grunde geneigt so was wie Result und Option aus Rust in
JavaScript nachzuahmen.
In JS nach dem Check "throw" nutzen. Schau dir auch das Error Objekt an.
In C würde ich Fehlercodes zurück geben und den Fehler loggen.
Beim reinen loggen von Sachen würde ich auch je nach Platform anders
vorgehen, In Java und JS gibt es recht gute & bekannte libs & interfaces
fürs loggen. Die Implementation ist dort glaube ich auch recht gut
austauschbar, oder zumindest, wohin geloggt wird. In C würde ich meine
eigenen Logging API definieren, eventuell Factory & Facade pattern
nutzen.
Beim loggen klassifiziert man oft, wie schlimm / wichtig etwas ist, zu
was es gehört, etc. Oft kann man irgendwie filtern, was geloggt werden
soll. Muss aber bei kleineren Sachen nicht unbedingt sein.
Bei einfachen Programmen reicht normalerweise loggen nach stdout /
stderr. Bei grösseren Sachen habe ich gerne zusätzlich die Möglichkeit
statt nach stdout / stderr direkt nach syslog zu loggen. Ein logger für
Dateien ist ein nettes Extra, aber ich bin kein grosser Fan davon, weil
schwieriger zu managen.
Das das Programm bei Fehlern beim Loggen nicht abschmiert oder hängt,
und eventuell was zum groben Vorfiltern (keine debug Meldungen), erwarte
ich von den Entwicklern.
Das das system mit unerwarteten Unmengen an Logs klarkommt, erwarte ich
hingegen vom Sysadmin. Minimal sollten Logfiles auf einer eigenen
Partition, oder einem Ort mit einer Quota abgelegt werden, damit dann
nur die Logs betroffen sind. Ob & wann man sie komprimiert, löscht, was
man filtert, ob man ein logging daemon mit rate Limiting hat, oder in
etwas wie z.B. elastic reinpumpt, etc. sind alles Sachen, um die sich
die Sysadmins kümmern müssen, und eventuell Firmenweit vorgegeben werden
sollten.
CleanCoder schrieb:> Wo gehören diese Fehlermeldungen hin?
Wie immer: Um das Problem soll sich "jemand anderes" kümmern. D.h. wird
ein Fehler festgestellt, wird der an den Aufrufer signalisiert. Der soll
sich drum kümmern. Also:
A() ruft B() auf, in B() entsteht ein Fehler, der wird an A()
gemeldet.
Der Aufrufer muss entscheiden, was mit dem Fehler passiert. Könnte ja
auch sein, dass der Aufrufer etwas anderes probieren möchte oder der
Fehler aus seiner Sicht in der aktuellen Situation überhaupt kein Fehler
ist.
Nötigenfalls reichert er den Fehler aber auch mit zusätzlichen und/oder
neuen Informationen an, und signalisiert ihn wiederum an seinen
Aufrufer. Irgendwann kommt der Fehler an eine Stelle an, wo es
eigentlich nicht mehr höher geht bzw. die Interaktion statt findet und
der Fehler dem Benutzer präsentiert werden kann.
Davon abzugrenzen sind Log-Dateien. Ganz weit verbreitet ist die
Marotte, an der Stelle wo ein Fehler auftritt, einen Log-Eintrag zu
erzeugen. Meiner Meinung nach ist das in den meisten Fällen nutzlos. Es
werden riesige Logfiles erzeugt, die kaum Informationen enthalten. Denn
meistens ist Kontext zu einem Fehler viel interessanter, als der Fehler
selbst. Und das fehlt diesen Logdateien.
Persönlich erstelle ich Log-Einträge an Schnittstellen. Eine typische
Schnittstelle ist z.B. ein API-Call über das Netzwerk, z.B. per HTTP.
Wenn da ein Fehlercode zurück kommt, kommt in das Log u.a. eben der
Fehlercode, aber auch die relevanten Parameter des Calls etc.
CleanCoder schrieb:> Sollen die> Fehler direkt in is_valid_obj() und is_command() ausgegeben werden oder> in setup_init()?
Die Funktionen/Methoden is_xyz() sind ziemlich sicher die absolut
falschen Stellen, eine Fehlermeldung auszugeben. Die sollen doch nur
Testen, ob irgendwas in Ordnung ist. Warum etwas nicht in Ordnung ist,
und ob das überhaupt ein (großes) Problem ist, wissen diese Funktionen
doch nicht. Der Aufrufer, der is_xyz() aufruft, muss entscheiden, wie
mit einem negativen Test zu verfahren ist, einschließlich der
Entscheidung, eine Meldung auszugeben oder auch nicht.
In verschiedene Sprachen übersetzte Fehlermeldungen lassen sich schlecht
im Internet suchen, da entstehen dann so lokalsprachlich zersplitterte
Infoblasen. Auch nicht toll ...
LG, Sebastian
CleanCoder schrieb:> Arbeite ich mit bool als Rückgabewerten? Doch die böse null nutzen?
Moderne (OOP) Sprachen haben dazu die Exception erfunden, damit eben
NICHT ein Rückgabewert als Fehlercode ausgesucht und nach jedem Aufruf
geprüft werden muss.
Ohne OOP hat man ein Problem, longjmp räumt nicht den geallocten
Speicher ab der an Pointern in lokalen Variablen hängt, die beim
Abräumen des stacks verloren gehen.
Sprachen, die garbage collection intern machen haben es da einfacher und
brauchen keine Klassen um Speicherobjekte zu verpacken.
CleanCoder schrieb:> In setup_init() wird is_valid_obj() aufgerufen und in is_valid_obj()> wird is_command() aufgerufen.
Daraus würde ich wohl machen:
is_command und is_valid_obj geben true/false zurück, weil die nicht
wissen können ob "nein" ein Fehler oder ein erwartetes Ergebnis des
Tests ist.
setup_init ruft die Tests auf und wirft bei einem Fehler eine Exception.
Darum kümmert sich dann der Aufrufer von setup_init.
Die eigentliche technische Fehlermeldung schreibe ich inzwischen immer
auf Englisch, weil meistens die Zusatzinformationen vom System auch auf
Englisch sind und man so mehr Hilfe googlen kann.
Die Meldung blubbert dann durch das Programm bis zu einer User-Seiten
Fehlerbehandlung. Die hat dann ein Fenster mit lokalisierter
Beschriftung "ein Fehler ist aufgetreten" und der original Nachricht.