2009-06-30 2 views
5

Я работаю над конфигурацией Log4Net, которая будет регистрировать все необработанные исключения. Мне нужно, чтобы на каждую запись журнала добавлялись определенные свойства, основанные на пользователе. Я успешно установил это в моем приложении Application_Error следующим образом. Вот мой полный global.asaxLog4Net, ThreadContext и 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 

Это, кажется, работает нормально. Однако у меня есть некоторые вопросы, на которые я не могу ответить.

Является ли способ добавления пользовательских свойств через threadcontext, правильно? Будет ли это всегда регистрировать правильную информацию, даже при загрузке? Когда вы будете использовать threadlogicalcontext? Есть лучший способ сделать это?

Благодаря

ответ

11

Это не безопасно для загрузки запроса конкретных значений в ThreadContext подобное. Причина в том, что общие потоки ASP.NET для запросов на обслуживание. На самом деле это происходит довольно часто.

Вы можете вместо этого использовать LogicalThreadContext, однако это просто сохраняет значения в Call Context, который используется для Remoting.

AFAIK не содержит контекстного хранилища HttpContext, поэтому вы можете вместо этого назначить экземпляр «поставщик значений» в качестве контекста потока, а во время выполнения он вызовет .ToString() в этом классе, чтобы получить значение.

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

Это не идеальный вариант, но он работает.

+1

Просто будьте осторожны, если вы используете параллельное кодирование, так как HttpContext.Current может потребоваться копировать на любые рабочие потоки, если они добавляются в журнал. –

+0

Где в global.asax.cs вы бы поместили вызов HttpContextUserProvider? – Rory

+0

ASP.Net имеет специфическое для контекста хранилище. См. Здесь: http://msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx – MatteoSp

5

Ответ Бена находится прямо на.

Однако, как и некоторые другие пользователи, я все еще немного потерял, как продолжить. Это сообщение log4net Context problems with ASP.Net thread agility, и особенно это Marek Stój's Blog - log4net Contextual Properties and ASP.NET, дайте еще один контекст для проблемы с некоторыми превосходными примерами кода.

Я очень рекомендую реализацию Marek Stój, хотя в моем случае нужно заменить ThreadContext.Properties["UserName"] на ThreadContext.Properties["User"].

Я добавил метод BeginRequest к моему классу Logger, который я вызываю из Application_AuthenticateRequest, который загружает все соответствующие свойства log4net.

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

И код метода:

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); 
} 

я обнаружил, что должен был пройти в объекте запроса, вместо использования HttpContext.Current.Request в рамках метода. В противном случае я потерял бы информацию пользователя и аутентификацию. Обратите внимание, что класс IPNetworking является моим собственным, поэтому вам нужно будет предоставить собственный метод получения IP-адреса клиента. Класс AdaptivePropertyProvider находится непосредственно от Marek Stój.

+1

Это присваивает объект адаптивного свойства коллекции свойств «TheadContext», но не была ли начальная проблема, что мы не могли рассчитывать на правильность этой коллекции? Я думаю, что адаптивный обработчик свойств должен быть назначен как свойство в глобальном контексте ... – starwed

+0

Я не уверен, какой глобальный объект применим, поскольку мы пытаемся зарегистрировать в данном случае данный веб-сеанс, поэтому значения будут контекстуализированы отдельным потоком, правильно? – Shane

Смежные вопросы