Datenbankzugriffsschicht ohne mit Singletons um sich zu schmeißen?
-
SideWinder schrieb:
Logger... schrieb:
Was bring do...while(false); ?
Schiebt das ganze in einen Block, verhindert bspw. das Loggen an "unüblichen" Bereichen wo das Makro schaden anrichten könnte.
Reicht da nicht {...} ?
-
Das würde wahrscheinlich reichen, wenn du kein Semikolon hinter den Makroaufruf setzt. Sonst bekommst du Probleme:
if(irgendwas) LOG("was auch immer"); else machWas();
Wenn du lediglich nen Block ohne Schleife in LOG hast, mutiert das Semikolon zu ner leeren Anweisung und das else gehört nicht mehr zum if.
-
BierzeltOmi schrieb:
volkard schrieb:
Die Frage stellt sich nicht wirklich, da per Makro geloggt wird, zum Beispiel
#define LOG(...) do{std::ofstream("log.txt",ios::app)<<__VA_ARGS__;}while(false)
ich hoffe das war jetzt beispielcode und ihr loggt nicht so in Produktivanwendungen
Es ist Beispielcode.
Aber welches Problem hast Du damit?
-
Auch wenn Makros im Allgemeinen eine schlechte Idee sind, für Logging sind sie praktisch, es gibt keinen andren Weg automatisch Dinge wie Datei + Zeilennummer mitzuloggen
Wobei ich mein Log auch oft per DI übergeb. Man braucht das Ding dann doch auch nur an wenigen Stellen.
-
volkard schrieb:
BierzeltOmi schrieb:
volkard schrieb:
Die Frage stellt sich nicht wirklich, da per Makro geloggt wird, zum Beispiel
#define LOG(...) do{std::ofstream("log.txt",ios::app)<<__VA_ARGS__;}while(false)
ich hoffe das war jetzt beispielcode und ihr loggt nicht so in Produktivanwendungen
Es ist Beispielcode.
Aber welches Problem hast Du damit?Habt ihr keine log level? Wie händelt ihr mehrere verschiedene Logger für verschiedene Teile des Systems? Wie konfiguriert ihr eure Logger? Mehrere Ausgabegeräte (Datei, Konsole, Windows Log, E-Mail, ...)? Thread-Sicherheit? Fail-safe gegenüber Festplatten-, Netzwerk- und Programmfehler (= Absturz)? Verschiedene Ausgabeformate (neben Ausgabegeräten), sprich Text, XML, JSON, human-readable (e-mail)? Internationalisierung/Lokalisierung?
Um nur mal ein paar Stichpunkte zu nennen die nicht-Bananensoftware eigentlich zu Kenntnis nehmen sollte. Und dann kommst du mit 1 (oder 2 oder 3) Zeilen Makros nicht mehr hin...
-
Liebe BierzeltOmi, wir reden hier nicht von Java oder C#. Es ist interessant, dass es überhaupt Logging gibt, ok?
MfG SideWinder
-
BierzeltOmi schrieb:
Wie händelt ihr mehrere verschiedene Logger für verschiedene Teile des Systems?
Dependency Injection
BierzeltOmi schrieb:
Wie konfiguriert ihr eure Logger? Mehrere Ausgabegeräte (Datei, Konsole, Windows Log, E-Mail, ...)?
Dependency Injection
BierzeltOmi schrieb:
Thread-Sicherheit?
Dependency Injection
All die genannten Punkte sind ja praktisch Paradebeispiele für Dinge bei denen Singletons massive Probleme verursachen...
-
Magst du mal ein konkretes Beispiel geben, wie so ein Dependency Injection Logger aussehen könnte?
-
SideWinder schrieb:
Ich habe den Thread nicht verfolgt, aber generell, aus Sicht der derzeitigen SE-Theorie, ist ein Singleton hier falsch. Überall herumzureichen auch. Dafür verwendet man im Normalfall Dependency Injection. Keine Ahnung ob sich das schon bis C++ herumgesprochen hat.
Alexandrescus Modern C++ Design zeigt für C++ angemessene Techniken, die mit Templates Inversion of Control erlauben und damit typsicher sind und keinen Laufzeitoverhead kosten. Wir benutzen entsprechend Dependency Injection nebst DI-Framework eher nicht. Und wir haben eine mächtig große Abneigung dagegen, Logik in Konfigurationsdateien zu stecken.
-
fdfdg schrieb:
Magst du mal ein konkretes Beispiel geben, wie so ein Dependency Injection Logger aussehen könnte?
class Log { private: Log(const Log&); Log& operator =(const Log&); protected: Log() {} ~Log() {} public: virtual void message(const std::string& msg) = 0; virtual void warning(const std::string& msg, int level) = 0; virtual void error(const std::string& msg, int level) = 0; };
-
Es gibt eine IMO gute Alternative zu Singletons, die sehr selten erwähnt wird. Was mich etwas erstaunt. Und das sind Thread-Locals.
Die eignen sich z.B. auch sehr schön für Logging.
Dadurch kann man nach wie vor über Makros und ohne irgendwas explizit rumreichen zu müssen auf den Logger zugreifen.
Gleichzeitig kann man aber für verschiedene Dinge verschiedene Logger verwenden, im selben Programm. Und auch so, dass eine Funktion F verschiedene Logger verwendet, abhängig davon zu welchem Zweck F gerade aufgerufen wird. Ohne dass man den Logger an Funktion F explizit übergeben müsste.
Wenn Thread X anfängt Request A zu bearbeiten, kann er nen Zeiger auf den passenden Logger in die Thread-Local Variable schreiben. Wenn Funktion F dann einen Logger braucht guckt sie einfach in der Thread-Local Variable nach welchen sie verwenden soll.
Wenn Thread X aufhört Request A zu bearbeiten, setzt er den Zeiger wieder zurück.
Und wenn gleichzeitig in Thread Y Request B bearbeitet wird, wo vielleicht ein ganz anderer Logger verwendet werden sollte, gibt es auch kein Problem.
Achja, mit "Logger" meine ich jetzt nicht notwendigerweise das Ding das die Log-Message in ein File schreibt. Kann auch gerne ein "Log-Channel" sein, der die Message erstmal filtert und dann ggf. an mehrere "Log-Writer" weiterreicht.
----
Fail-safe gegenüber Festplatten-, Netzwerk- und Programmfehler (= Absturz)?
Newsflash: das geht nicht.
Um nur mal ein paar Stichpunkte zu nennen die nicht-Bananensoftware eigentlich zu Kenntnis nehmen sollte.
Du übertreibst auch gar nicht gerne, oder?
-
volkard schrieb:
SideWinder schrieb:
Ich habe den Thread nicht verfolgt, aber generell, aus Sicht der derzeitigen SE-Theorie, ist ein Singleton hier falsch. Überall herumzureichen auch. Dafür verwendet man im Normalfall Dependency Injection. Keine Ahnung ob sich das schon bis C++ herumgesprochen hat.
Alexandrescus Modern C++ Design zeigt für C++ angemessene Techniken, die mit Templates Inversion of Control erlauben und damit typsicher sind und keinen Laufzeitoverhead kosten. Wir benutzen entsprechend Dependency Injection nebst DI-Framework eher nicht. Und wir haben eine mächtig große Abneigung dagegen, Logik in Konfigurationsdateien zu stecken.
Ja, wenn ich C++ professionell einsetzen würde, wäre das wohl auch eines der ersten Bücher die ich mir sofort zulege. Das verwechseln leider sehr viele bei Vergleichen zwischen C++ und Java/C#, dass die beiden Sprachen bis auf die Syntax und das Keyword "objektorientiert" nicht gar so viel gemeinsam haben. Mit "herumgesprochen" meinte ich jetzt nichts Böses. Wenn ich jedoch mit C++-Code in Berührung komme (nicht oft, und auch nicht hochprofessionell), dann aber leider meistens in Form von Templates=Generics und ansonsten eher Tooling + SE aus den 90ern, deswegen die Formulierung. Also schon etwas böse
Aber keine Ahnung wie das aussieht.
Inwiefern ich Logik in eine Konfigurationsdatei verstecke wenn ich dort nur festlege welche Implementierung eines Interfaces ich wähle? Hmm, ich weiß nicht inwiefern ich da Logik verstecke, aber gut.
MfG SideWinder
-
Das "ver" von "verstecke" habe ich gar nicht gesagt.
-
volkard schrieb:
Das "ver" von "verstecke" habe ich gar nicht gesagt.
Irgendwie vermutete ich unterbewusst, dass das ein Grund dagegen sein könnte. Ich glaube aber weiterhin, dass ich gar keine Logik in die Konfigurationsdatei stecke, dementsprechend also auch nichts verstecke.
MfG SideWinder
-
dot schrieb:
fdfdg schrieb:
Magst du mal ein konkretes Beispiel geben, wie so ein Dependency Injection Logger aussehen könnte?
class Log { private: Log(const Log&); Log& operator =(const Log&); protected: Log() {} ~Log() {} public: virtual void message(const std::string& msg) = 0; virtual void warning(const std::string& msg, int level) = 0; virtual void error(const std::string& msg, int level) = 0; };
Kann es sein, dass du Dependency Injection mit Interfaces verwechselst, dass Beispiel bringt mal garnichts.
-
Mach doch mal ein Beispiel, wo Klasse A in ein Logfile schreibt, Klasse B in ein Logfile und das Logwindow schreibt und Funktion C in ein Logfile und auf Console loggt.
-
DI wurde doch schon ausführlich diskutiert. Jedes Objekt das was loggen will bekommt nun eben so ein Interface!? Das Objekt welches ich übergeb kann nun in ein Logfile oder ein Logwindow oder was auch immer schreiben...
-
Mach doch mal das Beispiel von oben.
-
Was genau ist daran jetzt bitte noch unklar!?
-
Machst du sowas?
class KlasseB { public: KlasseB(Log fileLogger, Log windowLogger)... }
oder
class KlasseB { public: KlasseB(Log fileAndWindowLogger)... }