Forum: PC-Programmierung Clean Code: Wo gehören Fehlermeldungen hin?


von CleanCoder (Gast)


Lesenswert?

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?

von Klaus W. (mfgkw)


Lesenswert?

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?

von MaWin (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von PittyJ (Gast)


Lesenswert?

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

von Voodoopriester (Gast)


Lesenswert?

Jede grösser Software schreibt ein Log (oder mehrere), dafür gibts ne 
API oder was selber gestricktes. Was, wann, wie, musst du selber 
entscheiden.

von Manfred (Gast)


Lesenswert?

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 ...

von CleanCoder (Gast)


Lesenswert?

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.

von DPA (Gast)


Lesenswert?

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.

von Einer (Gast)


Lesenswert?

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.

von Sebastian (Gast)


Lesenswert?

In verschiedene Sprachen übersetzte Fehlermeldungen lassen sich schlecht 
im Internet suchen, da entstehen dann so lokalsprachlich zersplitterte 
Infoblasen. Auch nicht toll ...

LG, Sebastian

von MaWin (Gast)


Lesenswert?

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.

von Andre (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.