Laden...

Logging

Erstellt von joh.w vor 14 Jahren Letzter Beitrag vor 14 Jahren 1.405 Views
J
joh.w Themenstarter:in
140 Beiträge seit 2006
vor 14 Jahren
Logging

Hi,

habe mit ASP.Net 2.0 einen Webservice geschrieben, der von einem SAP System Daten extrahiert und dem Anfrager zurückgibt (kein WCF-Service). Je nach dem wie DAU der Benutzer ist, kann es sein, dass aufgrund seiner Parameterspezifikation etwas mehr Daten angefragt werden. Die Laufzeit des Webservice kann dann schon mal 20 Sekunden, eine Minute, ja sogar Stunden sind denkbar, dauern. Hängt auch etwas von der Performance des unterliegenden Systems an... sei es wie es sei.

Jetzt habe ich die Anforderung, die Anfragen mitzuprotokollieren. Einerseits, damit ich Systemseitig die Execution-Time mit in die Log-Datei schreibe und andererseits damit im Fehlerfall gezielt Bugfixing betrieben werden kann.

Im Moment habe ich dazu eine Logger-Klasse eingebaut, die ein Singleton ist. Weite Teile der Applikation nutzen diese Logger-Klasse um verschiedenst klassifizierte Meldungen abzugeben. Je nach Konfiguration des Webservice dann werden nur bestimmte Klassifizierungen angenommen, eine Log-Datei geschrieben, oder für Debugging-Zwecke anstelle des Results direkt an den Browser ausgegeben.

Problem: Es ist ein Singleton! Ich kann also nicht auswerten von welchem Request jetzt welcher Log-Eintrag her rührt.

Im Normalfall sollte es nicht vorkommen, dass der Dienst zur gleichen Zeit angesprochen wird. Bloß was ist schon Normalfall.

Also möchte ich gerne die erzeugen Log-Einträge dem Request spezifisch zuordnen können. Jetzt fehlen mir etwas die Ideen. Gibt es eine Standardtechnik um im Web solche Art von Log-Dateien zu erzeugen?

Hätte jetzt die Idee gehabt, dass ich an den Logger-Singleton bei jedem absetzen einer Message den Request mitgebe. Jedoch scheitert dies daran, dass ich in den Untiefen der Applikationsklassen gar nicht auf den Request komme. Und jetzt alles umbauen um den Request mitzuschleifen, will ich auch nicht (wäre die Holzhammermethode).

Vielleicht gibts jemand da draußen, der eine konstruktive Idee bringen kann.

Gruß
joh.w

T
433 Beiträge seit 2006
vor 14 Jahren

Hallo joh.w,

wenn ich dich recht verstehe willst du im WS loggen.
Dort hast du Zugriff auf Context und darunter befindet alles was du so brauchst, mit unter auch Request. Das übergibst du einer Methode in deinem Logger und wertest so aus was du benötigst.

Gruß,
Tom

4.506 Beiträge seit 2004
vor 14 Jahren

Hallo joh.w,

ist es Deine eigene Logger Klasse?

Wenn ja:

Dann erweitere Deine Logging-Klasse, dass man neben dem Logging-Text noch eine "Kategorie" angeben kann. Die Kategorie wird dann dem eigentlichen Loggingtext vorangestellt, oder es wird pro Kategorie eine separate Logging-Datei /-Datenbanktabelle angesprochen.

Dann kannst Du bei den Requests einen eindeutigen Identifizierer (z.B. IP Adresse) verwenden, und Du hast die Möglichkeit die Logging Ergebnisse den Kategorien zuzuordnen.

Vergleiche doch mit dem Ereignislog, bei dem man angeben muss, ob in "System", "Application", "Security" oder in eine eigene Kategorie geloggt wird.

Wenn nein:

Dann sag uns doch welche 😉

Grüße
Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

J
joh.w Themenstarter:in
140 Beiträge seit 2006
vor 14 Jahren

Hi Tom,

das ist zumindest ein Ansatz. Über System.Web.HttpContext.Current komme ich wohl auf den aktuellen. Ich bin mir nicht sicher, ob ich das schon mal versucht habe und wieder verworfen... aber dennoch scheint dieser Ansatz erst mal der einfachste zu sein. Danke.

Hi Timo,

ja, das ist meine eigene Logger-Klasse. Den Vorschlag den du bringst mit der Kategorie (ist für den Sinn den ich brauch dann aber auch eine Vergewaltigung dieses Parameters) funktioniert insofern nicht, da ich dann an jeder Stelle wo ich den Logger aufrufe IMMER die Kategorie mit angeben muss. An manchen Stellen im Code ist das einfach. An manch anderen bedeutet das sehr viel Code-Änderung. Da bin ich immer etwas dagegen, da das meist sehr fehleranfällig ist.

Werde erst mal obige Lösung, die von Tom vorgeschlagen wurde versuchen zu implementieren. Da müsste ich auch vorerst nur was an der Logger-Klasse selbst ändern.

Gruß
joh.w

T
433 Beiträge seit 2006
vor 14 Jahren

Wegen unterschiedlichen Kategorien könntest du deine bisherige Methode auch überladen und eine default Kategorie nutzen.
Somit müsstest du deinen bisherigen Code nicht ändern.

Gruß,
Tom

4.506 Beiträge seit 2004
vor 14 Jahren

Hallo zusammen,

Wegen unterschiedlichen Kategorien könntest du deine bisherige Methode auch überladen und eine default Kategorie nutzen.

So war das gemeint. Wenn keine Kategorie angegeben kommen die Logging Einträge in eine Standard Datei / Datenbanktabelle ...

Schnittstellen sollten generell immer nur erweitert werden, nicht geändert 😃

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

J
joh.w Themenstarter:in
140 Beiträge seit 2006
vor 14 Jahren

Ja, schon klar. Dennoch hätte ich überall eingreifen müssen, damit ich die Sache durchschleife. Also entweder ich änders gleich, dann hilft mir der Kompiler oder ich überlade dann vergesse ich möglicherweise an dieser und jener Stelle was.

Könnte ich zumindest verantworten, dass ich hier die Parameteranzahl der Methode verändere...

4.506 Beiträge seit 2004
vor 14 Jahren

Hallo joh.w,

für einen Entwicklertest kann man hier die Originalmethode gerne auskommentieren, dann solltest Du alle Stellen sehen, die betroffen wären.

oder ich überlade dann vergesse ich möglicherweise an dieser und jener Stelle was

Das ist bei einer Erweiterung immer der Fall. Zumindest wenn schon vorhandene Stellen die Erweiterung verwenden sollen.

Auch wenn Du es ohne Überladung lösen würdest, kämst Du in die gleiche Problematik etwas vergessen zu können (außer Du sperrst die Originalmethode).

Also Du gewinnst nichts, wenn Du keine Überladung anstrebst.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

J
joh.w Themenstarter:in
140 Beiträge seit 2006
vor 14 Jahren

Hi,

habe jetzt die Methode mit HttpContext.Current versucht. Dabei habe ich auch festgestellt, warum ich die damals wieder verworfen habe. Der HttpContext.Current ist NULL, wenn man darauf von einem unterliegendem Thread zugreift. Den Context krieg ich also nur, wenn ich in dem Thread darauf zugreife wo auch die Page ausgeführt wird.

Zuerst mal hab ich versucht, dass ich immer den Context abhole, indem ich einfach meine nötigen Schlüssel über den Thread ausführen lasse, wo auch die Page ausgeführt wird. Also mehr oder minder der Parent-Thread. Aber irgendwie geht das nicht.

Dann habe ich versucht, den Context an die Sub-Threads weiterzugeben. Das ist natürlich nicht gerade klug, denn ich müsste dennoch meine Sub-Thread-Methoden anpassen, dass dieser neue Parameter ausgewertet wird anstelle von HttpContext.Current und erschwerend kommt hinzu, dass es dabei wohl (laut Google-Suchen) beim GC zu ein paar Leaks kommt, da er das dann ab und an nicht richtig aufräumt.

Letztend Endes ist jetzt vorerst die Methode implementiert, die einfach den Identifier von der Page an alle anderen Objekte die darunter aufgerufen werden über Parameter weiterschleift. Äußerst miese Lösung - bin überhaupt nicht zufrieden damit.

Fazit:
Für ASP.Net, parallelen Requests und einer zentralen (Singleton-)Logger-Klasse gibt es keine Möglichkeit elegant und requestspezifisch Log-Meldungen auszuwerten.

Falls es noch weitere Ideen dazu gibt, bin ich natürlich offen dafür.

Gruß
joh.w

T
433 Beiträge seit 2006
vor 14 Jahren

Naja, wie willst du vernünftig auf HttpContext.Current in einem anderen Thread darauf zugreifen?
Läuft der Thread der Page noch und wartest du auf dem Thread in dem du gerade loggen willst? Ansonsten wüsste ich nicht wieso HttpContext.Current noch zur Verfügung stehen sollte wenn die Page ansich schon abgearbeitet ist und nur dein 'Worker Thread' noch am werkeln ist.

Entweder loggst du in dem Thread in dem die Page läuft, oder du ziehst benötigte Informationen mit. Alles andere macht in meinen Augen keinen Sinn.

Gruß,
Tom

4.506 Beiträge seit 2004
vor 14 Jahren

Hallo zusammen,

ich sehe das genauso wie Tom: Die relevanten ContextInformationen in eine eigene Datnstruktur (Klasse, Struct, oder nur String) kopieren und dem Workerthread und dem Loggen mitgeben.

Genau dafür bietet sich die Logging-Überladung an, die könnte als weiteren Parameter die ContextInformationen erwarten.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

J
joh.w Themenstarter:in
140 Beiträge seit 2006
vor 14 Jahren

Hi,

@Tom: Ja, die Page wartet auf Ende der Ausführung aller gestarteten Threads. Ist ein PageAsyncTask, der das letztlich aussteuert.

In meinen Augen macht es allerdings schon Sinn, den HttpContext an alle Kind-Threads bereit zu stellen. Wieso auch nicht? Der Origin ist doch von der Page.

Die Überladung bringt mir insofern nichts, da ich den zusätzlichen Parameter nur an den Stellen weglassen kann, wo die Logger-Klasse selbst den HttpContext feststellen kann, an den anderen Stellen wo ich dann keinen Zugriff auf den HttpContext hätte, muss ich dann den Parameter von außen setzen, den ich aber zu dem Zeitpunkt auch nicht habe.
Von daher ist es mir immer noch um längen lieber ich ändere gleich den Methodenaufruf und füge den zusätzlichen Parameter ein. Denn damit ist die Logger-Klasse in sich Konsistent und der Programmcode außenherum ist dafür zuständig alle Daten bereit zu halten. Das ist die leichteste Nachvollziehbarkeit.

Gruß
joh.w