Datenbankzugriffsschicht ohne mit Singletons um sich zu schmeißen?
-
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)... }
-
Ich denke auch, dass man Singletons nicht für DB-Verbindungen nutzen sollte. Ich hab hier eine historisch gewachsene Software bei der die Instanz der DB-Verbindung immer von der obersten Ebene als Parameter an die nächst tiefere reingereicht wird. Es gab immer nur eine Instanz der DB-Verbindung und die änderte sich eigentlich auch nicht. Hab damals überlegt, ob man die Verbindung auch in einem Singleton unterbringen und in den entsprechenden Funktionen abfragen könnte. Bloß gut, dass wir das nicht gemacht haben.
Zwei Jahre später kam nämlich heraus, dass unter bestimmten Umständen (und an ganz bestimmten Stellen) eine andere DB-verbindung (mit anderem Zeichensatz) zur selben DB benötigt wird. Die können wir ganz bequem an den entsprechenden Stellen reinreichen und alles Funktioniert wie bisher. Bei der singleton-Variante hätten wir erst den Code und die möglichen Seiteneffekte analysieren müssen.
Mir persönlich gefällt das Reinreichen der Instanz in jede Funktion aber auch nicht.
ähm schrieb:
Machst du sowas?
class KlasseB { public: KlasseB(Log fileLogger, Log windowLogger)... }
oder
...Das würde mich auch mal interessieren. Kann ja nicht schaden mal eine Alternative zu sehen.
-
dot schrieb:
BierzeltOmi schrieb:
Thread-Sicherheit?
Dependency Injection
Quatsch. Nur weil du die Klasse übergibst ändert das nichts. Du musst immer noch locken vor du von verschiedenen Threads ins gleiche Log-File schreibst.
-
yxcvbnm schrieb:
dot schrieb:
BierzeltOmi schrieb:
Thread-Sicherheit?
Dependency Injection
Quatsch. Nur weil du die Klasse übergibst ändert das nichts. Du musst immer noch locken vor du von verschiedenen Threads ins gleiche Log-File schreibst.
Das musst du aber in jedem Fall, daher ging ich davon aus dass BierzeltOmi das nicht gemeint haben kann denn das wäre ja sinnlos. Bei der DI Variante ist zumidest immer klar definiert wer wann welches Log verwendet...