2009-06-30 11 views
5

Ich arbeite an einer Log4Net-Konfiguration, die alle nicht behandelten Ausnahmen protokolliert. Ich brauche bestimmte Eigenschaften, basierend auf Benutzer, zu jedem Protokolleintrag hinzugefügt werden. Ich habe dies erfolgreich in der folgenden Weise in meinem Application_Error-Ereignis eingerichtet. Hier ist meine komplette global.asaxLog4Net, ThreadContext und Global.asax

Imports log4net 
Imports log4net.Config 

    Public Class Global_asax 
     Inherits System.Web.HttpApplication 

     'Define a static logger variable 
     Private Shared log As ILog = LogManager.GetLogger(GetType(Global_asax)) 

     Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) 
      ' Fires when the application is started 
      ConfigureLogging() 
     End Sub 

     Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) 
      ' Code that runs when an unhandled error occurs 
      Dim ex As Exception = Server.GetLastError() 
      ThreadContext.Properties("user") = User.Identity.Name 
      ThreadContext.Properties("appbrowser") = String.Concat(Request.Browser.Browser, " ", Request.Browser.Version) 
      If TypeOf ex Is HttpUnhandledException AndAlso ex.InnerException IsNot Nothing Then 
       ex = ex.InnerException 
      End If 
      log.Error(ex) 
      ThreadContext.Properties.Clear() 
     End Sub 

     Private Sub ConfigureLogging() 
      Dim logFile As String = Server.MapPath("~/Log4Net.config") 
      log4net.Config.XmlConfigurator.ConfigureAndWatch(New System.IO.FileInfo(logFile)) 
      log4net.GlobalContext.Properties("appname") = System.Reflection.Assembly.GetExecutingAssembly.GetName.Name 
     End Sub 
    End Class 

Dies scheint zu funktionieren. Ich habe jedoch einige Fragen, die ich nicht beantworten kann.

Ist der Weg, dass ich die benutzerspezifischen Eigenschaften über den threadcontext hinzufügen, korrekt? Wird dies immer die richtigen Informationen protokollieren, auch unter Last? Wann würden Sie threadlogicalcontext verwenden? Gibt es einen besseren Weg, dies zu tun?

Dank

Antwort

11

Es ist nicht sicher anforderungsspezifische Werte in ThreadContext so zu laden. Der Grund dafür ist, dass ASP.NET Threads für Serviceanforderungen freigegeben hat. Das tut es ziemlich oft.

Sie könnten stattdessen LogicalThreadContext, aber die einfach speichert die Werte in Anrufkontext, der für Remoting verwendet wird.

In AFAIK gibt es keinen HttpContext-spezifischen Kontextspeicher. Sie können stattdessen eine "value provider" -Instanz als Threadkontext zuweisen, und zur Laufzeit wird .ToString() für diese Klasse aufgerufen, um den Wert abzurufen.

public class HttpContextUserProvider 
{ 
    public override string ToString() 
    { 
     return HttpContext.Current.User.Identity.Name; 
    } 
} 

Es ist weniger als ideal, aber es funktioniert.

+1

Seien Sie vorsichtig, wenn Sie eine parallele Codierung verwenden, da HttpContext.Current möglicherweise in alle Worker-Threads kopiert werden muss, wenn sie zum Protokoll hinzugefügt werden. –

+0

Wo in global.asax.cs würden Sie den Aufruf auf HttpContextUserProvider setzen? – Rory

+0

ASP.Net verfügt über einen kontextspezifischen Speicher. Siehe hier: http://msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx – MatteoSp

5

Bens Antwort ist richtig.

Allerdings war ich, wie einige der anderen Benutzer, noch ein wenig verloren, wie es weiter geht. Diese log4net Context problems with ASP.Net thread agility Post und vor allem diese Marek Stój's Blog - log4net Contextual Properties and ASP.NET geben einige mehr Kontext für das Problem mit einigen ausgezeichneten Codebeispiele.

Ich empfehle Marek Stój Implementierung, obwohl die ThreadContext.Properties["UserName"] in meinem Fall mit ThreadContext.Properties["User"] ersetzt werden musste.

Ich habe meiner Logger-Klasse eine BeginRequest-Methode hinzugefügt, die ich von Application_AuthenticateRequest aufruft, die alle relevanten log4net-Eigenschaften lädt.

protected void Application_AuthenticateRequest(object sender, EventArgs e) 
{ 
    Logger.BeginRequest(Request); 
} 

Und der Methodencode:

public static void BeginRequest(System.Web.HttpRequest request) 
{ 
    if (request == null) return; 

    ThreadContext.Properties["ip_address"] = AdaptivePropertyProvider.Create("ip_address", IPNetworking.GetMachineNameAndIP4Address()); 
    ThreadContext.Properties["rawUrl"] = AdaptivePropertyProvider.Create("rawUrl", request.RawUrl); 

    if (request.Browser != null && request.Browser.Capabilities != null) 
     ThreadContext.Properties["browser"] = AdaptivePropertyProvider.Create("browser", request.Browser.Capabilities[""].ToString()); 

    if (request.IsAuthenticated && HttpContext.Current.User != null) 
     ThreadContext.Properties["User"] = AdaptivePropertyProvider.Create("user", HttpContext.Current.User.Identity.Name); 
} 

Ich fand ich stattdessen im Request-Objekt übergeben musste HttpContext.Current.Request innerhalb des Verfahrens zur Verwendung. Sonst würde ich die Benutzer- und Authentifizierungsinformationen verlieren. Beachten Sie, dass die IPNetworking Klasse meine eigene ist, so dass Sie Ihre eigene Methode zum Abrufen der Client-IP bereitstellen müssen. Die AdaptivePropertyProvider Klasse ist direkt von Marek Stój.

+1

Dadurch wird das adaptive Eigenschaftsobjekt der 'TheadContext'-Eigenschaftengruppe zugewiesen, aber war das nicht das ursprüngliche Problem, dass wir nicht damit rechnen konnten, dass diese Sammlung korrekt persistiert? Ich denke, der adaptive Property-Handler muss als Eigenschaft im globalen Kontext zugewiesen werden ... – starwed

+0

Ich bin nicht sicher, welche Art von globalem Objekt anwendbar wäre, wenn wir versuchen, in diesem Fall eine bestimmte Web-Sitzung zu protokollieren. also würden die Werte durch den einzelnen Thread kontextualisiert werden, richtig? – Shane