Log-System in einer multithreaded Anwendung
-
Hallo,
ich möchte mir gerne ein LogSystem bauen, dass in einer Umgebung mit mehreren Threads funktioniert. Das heißt: es soll aus mehreren Threads in unterschiedlichen Klassen in ein LogFile geschrieben werden. Ich habe mir vorgestellt das LogSystem als Singleton zu implementieren. Bin aber für andere Ideen offen...
Mein Ansatz(Header):
class LogSystem { public: static LogSystem& getInstance(); void logEntry(const std::string &strLogMessage); private: LogSystem(); ~LogSystem(); LogSystem(const LogSystem& other){} std::ofstream m_LogFile; boost::mutex m_mutex; };Mein Ansatz(Source):
LogSystem::LogSystem() :m_LogFile("app.log") { } LogSystem::~LogSystem() { m_LogFile.close(); } LogSystem& LogSystem::getInstance() { static LogSystem instance; return instance; } void LogSystem::logEntry(const std::string &strLogMessage) { boost::mutex::scoped_lock lck(m_mutex); if(m_LogFile.good()) { m_LogFile << strLogMessage << std::endl; } }Probleme:
1. Ich habe keinen Konstruktor, dem ich einen LogFile Namen mitgeben kann.
2. Ich weiß nicht, ob das Singleton so wie es hier implementiert ist in multithreaded Anwendungen zu Problemen führen kann.Ein weiterer Ansatz ohne Singleton wäre das LogSystem aus dem Manager-Thread (Hauptthread) zu erzeugen und über "extern" den Klassen bzw Instanzen, die etwas loggen dürfen/sollen zugänglich zu machen...
Habt ihr vielleicht weitere Ideen bzw habt ihr Verbesserungsvorschläge für obigen Code?
Vielen Dank für eure Hilfe.
Gruß
Robert
-
Ein weiterer Ansatz ohne Singleton wäre das LogSystem aus dem Manager-Thread (Hauptthread) zu erzeugen und über "extern" den Klassen bzw Instanzen, die etwas loggen dürfen/sollen zugänglich zu machen...
Genauso hab ich das bei mir gemacht. Eine Logger-Klasse, die einen eigenen Thread startet und ne threadsafe Queue hat, in die die anderen Threads reinschreiben. Hatte keine Probleme damit.
-
RobertH schrieb:
2. Ich weiß nicht, ob das Singleton so wie es hier implementiert ist in multithreaded Anwendungen zu Problemen führen kann.
Die Compilerbauer machen aus Eigenen Stücken den Meyers-Singleton (Um genau zu sein, das Initialisieren lokaler static-Variablen)Threadsicher. GCC hat gemacht. Ob MS auch so weit ist, weiß ich nicht, nehme es aber an.
-
RobertH schrieb:
Probleme:
1. Ich habe keinen Konstruktor, dem ich einen LogFile Namen mitgeben kann.Normalerweise schreibt man nicht error.log und warnung.log, sonder nur das log mit "error: gdsbsdghuipdsgisdgsdgbuz" und "warning: gusdisbgduisdgbzhsdp".
Also brauchst Du wirklich Dateinamen?Wenn ja, bau Dir halt eine
static LogSystem& getInstance(std::string fileName);eine map<string,ofstream*> dahinter und mutex oder critical section.
-
Also ich mach solche Dateinamen immer über die .ini konfigurierbar... Genauso wie das Unterverzeichnis, wo die Logs möglicherweise reingeschrieben werden sollen.
Gehört aus meiner Sicht zum guten Ton. D.h. ich muss mindestens das Config-Objekt übergeben, oder eben den Dateinamen direkt.
-
It0101 schrieb:
Gehört aus meiner Sicht zum guten Ton. D.h. ich muss mindestens das Config-Objekt übergeben, oder eben den Dateinamen direkt.
Puh, da haben wir aber Glück gehabt, daß das Config-Objekt seinerseits ein Singleton ist und gar nicht übergeben werden muß.
-
Wenn ja, bau Dir halt eine
static LogSystem& getInstance(std::string fileName);eine map<string,ofstream*> dahinter und mutex oder critical section.
Verstehe nicht, was Du meinst. Es geht mir nur um den File-Namen des Log Files, nicht um die warnings und errors, die im Log File stehen.
Aber die Idee den Log Filenamen in einem ini-File festzulegen klingt gut.Genauso hab ich das bei mir gemacht. Eine Logger-Klasse, die einen eigenen Thread startet und ne threadsafe Queue hat, in die die anderen Threads reinschreiben. Hatte keine Probleme damit.
Hmmm, ja das klingt ok. Ich habe allerdings nicht vor im LogSystem einen Thread laufen zu lassen. Wozu?
Viele Grüße
Robert
-
Es macht Sinn, das Schreiben auf das LogFile vom Rest der Anwendung zu enkoppeln. Daher: Eine threadsafe Queue in die alle Ihre Logmessages rein schreiben und ein separater Thread, der da raus liest und das Schreiben ins File übernimmt. Dadurch lässt sich der Aufwand für das Schreiben ins File vom Rest der Anwendung entkoppeln.
-
Es macht Sinn, das Schreiben auf das LogFile vom Rest der Anwendung zu enkoppeln. Daher: Eine threadsafe Queue in die alle Ihre Logmessages rein schreiben und ein separater Thread, der da raus liest und das Schreiben ins File übernimmt. Dadurch lässt sich der Aufwand für das Schreiben ins File vom Rest der Anwendung entkoppeln.
Wie kann man das verstehen. Wird die Queue von dem Thread dann schlicht gepollt in gewissen Zeitabständen oder wird der Thread über ein Event aufgeweckt, wenn etwas in die Queue geschrieben wird. Zweiteren Ansätz würde ich verstehen, wenn Du im Hinblick auf den Aufwand argumentierst..
Viele Grüße
Robert
-
RobertH schrieb:
Event
samephore

-
volkard schrieb:
RobertH schrieb:
Event
samephore

fast

-
wie schreibt ihr eigtl in eure log-systeme?
op<< (falls ja, wie kennzeichnet ihr den anfang/das ende eines logeintrages) oder fkt.
falls fkt, was nehmen die? nen string? nen stream? oder nen template und dann spezialisieren bzw. über stringstream?bb
-
Wie kann man das verstehen.
Producer Consumer Problem ...
Probleme:
1. Ich habe keinen Konstruktor, dem ich einen LogFile Namen mitgeben kann.Meine Singletons haben einen Konstruktor. Aber du kannst genauso gut eine int-Methode benutzen. Normalerweise benutze ich aber keine Klasse fuer etwas "Singleton"-artiges.
-
Die Queue wird über einen Mutex geschützt.
Der Thread schaut, wie Du vermutest, nach, ob die Queue nicht leer ist. Steht etwas drin, wird geschrieben, solange etwas in der Queue steht. Ist sie leer, legt sich der Thread für eine gewisse Zeit schlafen
Hier mal ein Beispiel
void Logger::run() { isTerminated = false; // Thread runs until terminateLogging() is called while( !shallTerminate ) { bufferLock.lock(); while ( !buffer.empty() ) { string s = buffer.front(); buffer.pop(); bufferLock.unlock(); // while writing to log file, give others a chance to write to the queue // decorateMessage(s); // add date and timestamp logFile << s.c_str() << "\n"; // flush later, hence no std::endl but "\n" bufferLock.lock(); } bufferLock.unlock(); usleep(1000); // if buffer is empty, go to sleep for 1ms } // Now we shall terminate. Clean up: // - Empty the queue. // - Keep the lock, so no other thread can write to the queue bufferLock.lock(); while ( !buffer.empty() ) { string s = buffer.front(); buffer.pop(); logFile << s.c_str() << "\n"; } logFile.flush(); isTerminated = true; }