Designfrage - Logging
-
Vielen Dank für deine Antwort!
Ich versuche von Makros möglichst Abstand zu halten, das einzige, was ich mit meinem Projekt mit dem Präprozessor mache, sind die Include-Guards.
Ich möchte auch im Release-Mode komplette Logs, bei einem IRC Bot ist das auch nicht so teuer, da Nachrichten ja sowieso nicht so schnell ankommen.Zu deinem Edit: Das ist natürlich eine Sache, über die ich noch nicht nachgedacht habe. Allerdings wüsste ich gerade nichts, was ich Loggen möchte und nicht nach string konvertierbar ist. Zur Not könnte ich ja meine eigenen Konvertierungsfunktionen definieren.
-
314159265358979 schrieb:
Allerdings wüsste ich gerade nichts, was ich Loggen möchte und nicht nach string konvertierbar ist. Zur Not könnte ich ja meine eigenen Konvertierungsfunktionen definieren.
Muß es denn soo langsam werden? Es würde schon mit variadic templates gehen, die Zwangskonvertierung loszuwerden. Hast Du nicht neulich noch geklagt, daß es da Frammeworks/Sprachen gibt, wo alles ein Bißchen komisch ist? Ich kenne da eins, da hat die Zwangsbasisklasse aller Klassen namens Object die virtuelle Methode ToString, um genau solche Schnittstellen zu ermöglichen.
Viele Wege führen von Rom weg.debugLog()<<getUserName()<<" logged in from "<<getPeerAddress()oder so ähnlich hätte ich wohl letzes Jahr geschrieben. Viele Möglichkeiten, ich kann schlecht raten, welche Aufrufsyntax Du am liebsten magst.
Aber wer hat Dir den Perfektionismus geklaut?
-
Auf die Idee mit dem Variadic Template bin ich gar nicht gekommen, das klingt gut

Irgendwie komm ich mir jetzt selbst doof vor.
-
Ich habe meinen Logger nun umgebaut. Ist es so besser?
template <typename Head> void write_to_stream(std::ostream& os, Head&& head) { os << head; } template <typename Head, typename... Tail> void write_to_stream(std::ostream& os, Head&& head, Tail&&... tail) { os << head; write_to_stream(os, std::forward<Tail>(tail)...); } class file_logging_provider : boost::noncopyable { std::ofstream os; protected: file_logging_provider() : os("bot.log") { if(!os.is_open()) throw file_error("failed to open file bot.log"); } template <typename... Args> void write_line(Args&&... args) { write_to_stream(os, std::forward<Args>(args)...); if(!(os << std::endl)) throw file_error("failed to write to file bot.log"); } }; class console_logging_provider : boost::noncopyable { protected: console_logging_provider() {} template <typename... Args> void write_line(Args&&... args) { write_to_stream(std::cout, std::forward<Args>(args)...); std::cout << std::endl; } }; template <typename LoggingProvider> class logger_ : private LoggingProvider { boost::mutex mtx; template <typename... Args> static void log(Args&&... args) { const auto local_time = boost::posix_time::microsec_clock::local_time(); const std::string time_string = boost::posix_time::to_simple_string(local_time); { const boost::mutex::scoped_lock lock(instance().mtx); instance().write_line('[' + time_string + "] ", std::forward<Args>(args)...); } } logger_() {} static logger_& instance() { static logger_ logger; return logger; } public: template <typename... Args> static void server (Args&&... args) { log("<Server> :: ", std::forward<Args>(args)...); } template <typename... Args> static void client (Args&&... args) { log("<Client> :: ", std::forward<Args>(args)...); } template <typename... Args> static void error (Args&&... args) { log("<Error> :: ", std::forward<Args>(args)...); } template <typename... Args> static void warning(Args&&... args) { log("<Warning> :: ", std::forward<Args>(args)...); } template <typename... Args> static void info (Args&&... args) { log("<Info> :: ", std::forward<Args>(args)...); } }; typedef logger_<console_logging_provider> logger;
-
314159265358979 schrieb:
Ich habe meinen Logger nun umgebaut. Ist es so besser?
Mach mal Aufrufbeispiele.
-
logger::info("connecting to ", iterator->host_name(), ':', iterator->service_name(), " (", iterator->endpoint().address(), ") ...");logger::warning("failed to read from socket: ", ec.message());logger::server(line);Aus meinem code entnommen. Stark verändert hat sichs nicht, etwas einfacher ist es geworden.
-
Jo, damit könnte ich leben.
Mit dem Provider habe ich noch ein Problem. Und daß man privat von seinem Template-Argument erbt. Sollte der Provider nicht nur einen ostream providen? st das Absicht, daß der logger kein globales Objekt ist? Das wäre für mich logisch.
Bleibe ich mal bei dieser Aufrufsyntax, müßte da logger nicht ein namespace sein und die static Funktionen freie Funktionen sein? Die können sich ja dann trotzdem noch eines Meyers-Singletons bedienen. Aber wozu? Den logger einfach static in die log() tun, und schon wieder ein komplexes Muster abgeschossen. Gehört der Mutex nicht zum Provider? Ist gar der Provider das funktionslokale static Objekt? Und wird nicht nur der provider getypedeft? Ich würde da noch viel Schnipseln, kleinermachen, umräumen und so. Und dann einen Lecker Makro drum.

Muß denn per typedef geschaltet werden? Der wird am Ende doch mit Umkommentierung umgeschaltet, dann kanns auch jede andere Weise sein, wo umkommentiert wird.ostream& getLogStream(){ using std::clog; // static ofstream clog("log.txt");//Ätsch, ich nehme doch eine Datei return clog; }Och nööö, jetzt fällt ja alles zusammen. Sorry.
-
Und was ist jetzt die "Message" aus deinem Post? Alles Kacke?
-
314159265358979 schrieb:
Und was ist jetzt die "Message" aus deinem Post? Alles Kacke?
Nö. Am Ende mußte ich zwar lachen, aber bei den anderen Loggern muß ich immer weinen.
Brauchst auch bei Fehler evtl nicht selber zu throwen. Vielleicht tut das da es auch
http://www.cplusplus.com/reference/iostream/ios/exceptions/
-
volkard schrieb:
Nö. Am Ende mußte ich zwar lachen, aber bei den anderen Loggern muß ich immer weinen.
Darf ich das so verstehen, dass mein Logger zwar brauchbar und verbesserbar, aber überdurchschnittlich "schön" ist?
volkard schrieb:
Brauchst auch bei Fehler evtl nicht selber zu throwen. Vielleicht tut das da es auch
http://www.cplusplus.com/reference/iostream/ios/exceptions/Ich werfe lieber eigene Exceptions, da ich so im Fehlerfall feststellen kann, dass der Fehler zumindest bedacht wurde. Wenn dann eine andere Exception geworfen wird, bemerke ich einen unbekannten Fehler und kann die Behandlung einbauen.
-
Das kam bei meiner ersten Schrumpfkur raus.
#include <cstdlib> #include <iostream> #include <fstream> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread.hpp> template <typename Head> void write_to_stream(std::ostream& os, Head&& head) { os << head; } template <typename Head, typename... Tail> void write_to_stream(std::ostream& os, Head&& head, Tail&&... tail) { os << head; write_to_stream(os, std::forward<Tail>(tail)...); } namespace logger { std::ostream& getLogStream()//in die logger.cpp { using namespace std; static std::ofstream clog("log.txt");//Ätsch, ich nehme doch eine Datei return clog; } boost::mutex mtx; template <typename... Args> void log(Args&&... args) { //auskommentiert, weil boost gerade nicht geht // const auto local_time = boost::posix_time::microsec_clock::local_time(); // const std::string time_string = boost::posix_time::to_simple_string(local_time); std::ostream& log=getLogStream(); { const boost::mutex::scoped_lock lock(mtx); write_to_stream(log,'[' , "time_string" , "] ", std::forward<Args>(args)...); } } template <typename... Args> void server (Args&&... args) { log("<Server> :: ", std::forward<Args>(args)...); } template <typename... Args> void client (Args&&... args) { log("<Client> :: ", std::forward<Args>(args)...); } template <typename... Args> void error (Args&&... args) { log("<Error> :: ", std::forward<Args>(args)...); } template <typename... Args> void warning(Args&&... args) { log("<Warning> :: ", std::forward<Args>(args)...); } template <typename... Args> void info (Args&&... args) { log("<Info> :: ", std::forward<Args>(args)...); } }//namespace logger int main() { logger::client("hallo ","welt!"); }
-
Sieht gut aus

Allerdings...
- Ich kann meine eigenen Exceptions nicht werfen. Das wäre mir doch ganz wichtig
- Vielleicht sollte man den mutex static machen
-
314159265358979 schrieb:
- Vielleicht sollte man den mutex static machen
Oder in die logger.cpp stopfen.
314159265358979 schrieb:
- Ich kann meine eigenen Exceptions nicht werfen. Das wäre mir doch ganz wichtig

Mhmm. Ist das Aufgabe des Loggers oder brauchst Du einen Wrapper um Files, weil Du bei Files immer Deine eigenen Exceptions werfen willst?
Oder mach irgendwas Kompliziertes.
-
volkard schrieb:
Oder in die logger.cpp stopfen.
Geht das denn so ohne weiteres? Woher weiß der Header, dass der in der .cpp gemeint ist? Eine extern-Deklaration würde den Mutex dann auch für alle, die den Header includen bekannt machen, oder nicht?
volkard schrieb:
Mhmm. Ist das Aufgabe des Loggers oder brauchst Du einen Wrapper um Files, weil Du bei Files immer Deine eigenen Exceptions werfen willst?
Oder mach irgendwas Kompliziertes.Ich möchte das eigentlich immer bei Files machen, auch bei Sockets mache ich das. Was schlägst du vor?
-
So, ich habe den Logger nun nochmal überarbeitet. Ich habe mich an deinem orientiert, ihn allerdings ein wenig anders gebaut. Hier einmal der Code...
namespace logger { namespace detail { std::ostream& logger_stream(); boost::mutex& logger_mutex(); template <typename... Args> void log(Args&&... args) { const auto local_time = boost::posix_time::microsec_clock::local_time(); const std::string time_string = boost::posix_time::to_simple_string(local_time); { const boost::mutex::scoped_lock lock(logger_mutex()); write_to_stream(logger_stream(), '[', time_string, "] ", std::forward<Args>(args)...); } } } template <typename... Args> void server (Args&&... args) { detail::log("<Server> :: ", std::forward<Args>(args)...); } template <typename... Args> void client (Args&&... args) { detail::log("<Client> :: ", std::forward<Args>(args)...); } template <typename... Args> void error (Args&&... args) { detail::log("<Error> :: ", std::forward<Args>(args)...); } template <typename... Args> void warning(Args&&... args) { detail::log("<Warning> :: ", std::forward<Args>(args)...); } template <typename... Args> void info (Args&&... args) { detail::log("<Info> :: ", std::forward<Args>(args)...); } }.cpp
namespace logger { namespace detail { std::ostream& logger_stream() { static std::ofstream logger("bot.log"); static bool dummy = [&logger]() { logger.exceptions(std::ofstream::badbit | std::ofstream::failbit); return true; }(); (void)dummy; return logger; } boost::mutex& logger_mutex() { static boost::mutex mtx; return mtx; } } }Was mir nicht gefällt, ist der weg, wie ich hier Exceptions aktiviere. Ich würde ja eine Funktion create_logger_stream() machen, die das erledigt, aber leider lassen sich Streams nicht mal moven

-
Hihi, da hab ich die Lösung doch glatt gefunden, direkt nachdem ich gepostet habe. Sieht nun so aus...
std::ostream& create_logger_stream() { static std::ofstream logger("bot.log"); logger.exceptions(std::ofstream::badbit | std::ofstream::failbit); return logger; } std::ostream& logger_stream() { static std::ostream& logger = create_logger_stream(); return logger; }