Seite 1 von 1

Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Di Feb 02, 2010 7:04 pm
von Bebu
Hallo aus Südamerika,

ich stehe mal wieder vor einem Problem, bei dem ich einen Denkanstoß brauche. Vor kurzem habe ich unter Projekte meine Logfileklasse vorgestellt. Erstmal ein bisschen Code um das Problem zu verdeutlichen:

Code: Alles auswählen

int sqlite::check(int ret_value)
{
    log.add("Entering check()");
    switch(ret_value)
    {
        case DATATYPE_NOT_HANDLED : log.add("SQL-W: Datatype not handled!");
                                    throw std::runtime_error(
                                      "Wrapper Error: Datatype not handled!");
                                    break;

        case DATATYPE_UNKNOWN     : log.add("SQL-W: Datatype unknown!");
                                    throw std::runtime_error(
                                          "Wrapper Error: Datatype unknown!");
                                    break;

        case SQLITE_OK            : log.add("SQL-W: SQLITE_OK");
                                    return WRAPPER_OK;
                                    break;
        default                   : log.add("SQL-W:SQL-Error:" +
                                             *sqlite3_errmsg(db_handle));
                                    throw std::runtime_error(
                                               sqlite3_errmsg(db_handle));
    }
    log.add("Leaving check()");
    return WRAPPER_ERROR;
}

Dieser Code stammt aus der aktuellen Version meiner SQLitewrapper Klasse. Mein Problem besteht jetzt darin, das zwar die throw Anweisungen problemlos befolgt werden, aber die Fehlermeldung die über log.add geschrieben werden soll, nicht mehr in der Datei landet.

Die aktuelle Version von log.add sieht so aus:

Code: Alles auswählen

void logfile::add(const std::string &input)
{
    check_size();

    boost::posix_time::ptime pt =
                            boost::posix_time::second_clock::local_time();

    logfile::logstream << input << " "
                       << boost::posix_time::to_simple_string(pt)
                       << std::endl;
}
Hat jemand eine Idee, wie ich dieses Problem umgehen kann? Es wäre ja schließlich sehr hilfreich, wenn die Fehlermeldungen in der Logdatei stehen würden.

Gruß Bebu

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Di Feb 02, 2010 7:13 pm
von cloidnerux
Dieser Code stammt aus der aktuellen Version meiner SQLitewrapper Klasse. Mein Problem besteht jetzt darin, das zwar die throw Anweisungen problemlos befolgt werden, aber die Fehlermeldung die über log.add geschrieben werden soll, nicht mehr in der Datei landet.
Welche log.add Anweisung? Vor oder nach dem throw?
Und du solltest auch noch einen try/catch-Block in deiner "add" funktion einbauen.

Auch hatte ich schon, das ifstream mit folgendem Aufruf nicht funktionierte:

Code: Alles auswählen

ifstream >> var;
Vielleicht solltest du das mal mit Write oder so Probieren.

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Di Feb 02, 2010 7:25 pm
von Bebu
cloidnerux hat geschrieben:Welche log.add Anweisung? Vor oder nach dem throw?
Und du solltest auch noch einen try/catch-Block in deiner "add" funktion einbauen.
Ich meine die log.add vor dem throw, also müsste diese Funktion sauber ausgeführt werden. Deshalb will sich mir das mit dem Try Catch in einer Funktion, die vor dem throw steht, nicht so recht erschließen. Aber ich kann es mal versuchen. Das mit dem Write könnte schwierig werden, weil ich die Boost Variante der Streamklassen benutzte, ist also nicht einfach so austauschbar ohne große Teile umzuschreiben.

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Di Feb 02, 2010 10:22 pm
von Bebu
Also hier eine Zwischenmeldung: Der Try Catch Block in der Add Funktion hat erwartungsgemäß nicht funktioniert. Dieser Fehler ist ohnehin merkwürdig, weil noch ein Teil im Log auftaucht, der bei einer Exception gar nicht auftauchen dürfte, der korrekte Teil aber überhaupt nicht funktioniert. Gibt es eine Möglichkeit ungepuffert in einen ofstream zu schreiben?

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Di Feb 02, 2010 10:40 pm
von Dirty Oerti
Hehe. Ich denke es handelt sich hierbei um das selbe Problem wie bei der Fehlersuche mit Hilfe von Testausgaben. Sicher bin ich mir aber nicht, da ich den internen Ablauf deines Logsystems nicht kenne.
Und zwar:

Code: Alles auswählen

cout << "Ich bin ein Test";
// Kritischer Fehler direkt in der hier folgenden Anweisung (oder auch 2/3 Anweisungen weiter)
Bei diesem dargestelltem Fall wird die Ausgabe nicht mehr getätigt. Denn durch den kritischen Fehler beendet sich das Programm, bevor dessen Ausgaben auf die Standardausgabe gebracht wurden (die Ausgaben also synchronisiert wurden).
Und sobald ein Programm beendet ist gibt es auch nichts mehr aus.
Die Folge: Noch nicht ausgegebenes wird auch nicht mehr ausgegeben.

Um das zu umgehen muss man die Ausgabe "manuell" synchronisieren.

Die einzig mir dazu bekannte Lösung ist, einfach ein "\n" ans Ende anzuhängen.
Das führt - zumindest bei cout / cerr - dazu, dass die Ausgabe synchronisiert werden und somit auch dargestellt werden, wenn das Programm unmittelbar danach hops geht.

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Mi Feb 03, 2010 2:18 am
von Bebu
Danke für den Tipp, aber den habe ich als erstes ausprobiert. Weder ein \n noch ein endl ändern etwas.
Ich denke eher, das es ein Problem in der Stackauflösung ist, und die Funktionen nicht in der exakten Reihenfolge abgebaut werden. Ich bin mir nicht ganz sicher, aber wenn ich das mit den ungehandelten Exceptions richtig verstanden habe, werden nacheinander alle aufgerufen Funktionen abgebaut, bis ein catch gefunden wird, das den Fehler behandelt. Wird keiner gefunden, schmiert das Programm ab.

Ich habe drei Funktionen. Die äußerste ruft die Checkfunktion aus dem ersten Post auf. Die Checkfunktion ruft die Logfunktion auf und anschließend wird über throw eine Exception ausgelöst. Die Logfunktion vor dem throw kommt in der Datei gar nicht an. Andererseits wird der Logeintrag, der am Schluss der äußersten Funktion steht zu 70 % Prozent geschrieben, es fehlen die ersten 4 oder 5 Buchstaben, der Rest kommt an. Dieses Bild stimmt so überhaupt nicht mit der Theorie, die ich gelernt habe überein. Auf Wunsch kann ich euch das ganze Projekt hier reinstellen, das sind allerdings 5 Quellcodedateien und 4 Header, in die man sich erstmal hineinarbeiten muss. Ihr könnt auch gerne das Git Archiv haben, ich habe nur keinen Webspace zur Verfügung um es hochzuladen.

Das ist ein Problem das ich überhaupt nicht nachvollziehen kann, vielleicht hat einer von den Profis ja mehr Ahnung davon.

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Mi Feb 03, 2010 11:32 am
von Kerli
Ich glaub es wäre das einfachste wenn du ein vollständig kompilierbares Archiv zur Verfügung stellst. Welchen Compiler verwendest du denn eigentlich?

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Do Feb 04, 2010 6:29 pm
von Bebu
Sorry das mit dem vollständig kompilierbaren Archiv ist ein bisschen schwierig umzusetzen, da müsste ich die komplette Boost-Library mit hochladen. Ich benutze den GNU GCC Compiler unter Code-Blocks. Leider habe ich von Makefiles keine Ahnung, ich habe es nur als Projektdatei zur Verfügung. Im Anhang findest du den Code samt Projektdateien. Um es kompilieren zu können, brauchst man einige kompilierte Boostbestandteile, genau genommen date_time, filesystem und system. Die Linkerpfade müssen angepasst werden, die werden nicht übereinstimmen, weil ich die Boost unter meinem Homeverzeichnis liegen habe. Außerdem ist noch die libsqlite3 samt Header nötig. Unter Ubuntu kann man sie direkt über den Packetmanager installieren oder das Archiv unter http://www.sqlite.org/download.html herunterladen. Erfordert leider ein paar Umstände, aber da hängen mittlerweile doch ein paar fremde Librarys mit im Projekt.

In der Main wird absichtlich ein Fehler erzeugt, der eine Exception wegen eines ungültigen SQL-Kommandos auslöst. Das Programm legt automatisch einen Unterordner log an, dort sind die Logs zu finden. Beim Durchsehen der Log, wird der Fehler offensichtlich, zumindest bei mir.

Gruß Bebu

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Mo Feb 08, 2010 6:37 pm
von Kerli
Bebu hat geschrieben:Sorry das mit dem vollständig kompilierbaren Archiv ist ein bisschen schwierig umzusetzen
Das hab ich eh nicht gemeint. Ich wollte nur den gesamten Code mit irgendwas das mir das zu einer Executable bauen kann. Die Bibliotheken hab ich sowieso drauf. Und wozu gibt es apt-get :P

An deinem Problem sind zwei Sachen schuld. Erstens verstehe ich folgenden Code nicht so ganz:

Code: Alles auswählen

default: log.add("SQL-W:SQL-Error:" +
                         *sqlite3_errmsg(db_handle));
Du addierst zur Adresse des ersten String den nach 'int' konvertierten Wert des ersten Zeichen der Fehlermeldung. Da beginnt die Ausgabe dann eben zufälligerweise mitten in einem deiner anderen konstanten Strings ;) Richtig wäre es so:

Code: Alles auswählen

default: log.add(std::string("SQL-W:SQL-Error:") +
                         sqlite3_errmsg(db_handle));
Und das zweite Problem ist das eine nicht aufgefangene Exception undefiniertes Verhalten hervor ruft, und bei mir dabei zum Beispiel der Destruktor der sqlite Klasse nicht mehr aufgerufen wird. Ich bau dafür zb in der main Funktion immer einen try/catch Block ein der einfach alle unbehandelten Exceptions auffangt und die Fehlermeldung nach std::cerr ausgibt. Somit ist sichergestellt das der Stack immer korrekt abgebaut wird.

Re: Einen Datenstream auch bei einer Execption sicher schreiben

Verfasst: Mo Feb 08, 2010 7:47 pm
von Bebu
Vielen Dank an Kerli, es war tatsächlich der erste Fehler, jetzt funktioniert es so, wie es soll. Wäre ich aber selber nie draufgekommen. Das mit den ungehandelten Exceptions ist im Moment noch Absicht, aber das soll nicht so bleiben. Herzlichen Dank.