На основі детальних потреб ОР, пояснених у коментарях , існує більш відповідне рішення. ОП заявляє, що хоче додати користувацькі дані у свої журнали з log4net, дані, пов'язані із запитами.
Замість того, щоб загортати кожен виклик log4net у спеціальний централізований виклик журналу, який обробляє отримання даних, пов’язаних із запитом (на кожен виклик журналу), log4net пропонує контекстні словники для налаштування спеціальних додаткових даних для журналу. Використання цих словників дозволяє розмістити дані журналу запитів для поточного запиту на події BeginRequest, а потім відхилити їх у події EndRequest. Будь-який вхід між ними виграє від цих спеціальних даних.
І те, що не трапляється в контексті запиту, не намагатиметься реєструвати пов’язані з запитом дані, усуваючи необхідність перевірити наявність запиту. Це рішення відповідає принципу, який пропонував Арман Макгітарян у своїй відповіді .
Щоб це рішення працювало, вам також знадобиться додаткова конфігурація у ваших додатках log4net, щоб вони могли реєструвати ваші власні дані.
Це рішення можна легко реалізувати як модуль удосконалення користувальницького журналу. Ось зразок коду для цього:
using System;
using System.Web;
using log4net;
using log4net.Core;
namespace YourNameSpace
{
public class LogHttpModule : IHttpModule
{
public void Dispose()
{
// nothing to free
}
private const string _ipKey = "IP";
private const string _urlKey = "URL";
private const string _refererKey = "Referer";
private const string _userAgentKey = "UserAgent";
private const string _userNameKey = "userName";
public void Init(HttpApplication context)
{
context.BeginRequest += WebAppli_BeginRequest;
context.PostAuthenticateRequest += WebAppli_PostAuthenticateRequest;
// All custom properties must be initialized, otherwise log4net will not get
// them from HttpContext.
InitValueProviders(_ipKey, _urlKey, _refererKey, _userAgentKey,
_userNameKey);
}
private void InitValueProviders(params string[] valueKeys)
{
if (valueKeys == null)
return;
foreach(var key in valueKeys)
{
GlobalContext.Properties[key] = new HttpContextValueProvider(key);
}
}
private void WebAppli_BeginRequest(object sender, EventArgs e)
{
var currContext = HttpContext.Current;
currContext.Items[_ipKey] = currContext.Request.UserHostAddress;
currContext.Items[_urlKey] = currContext.Request.Url.AbsoluteUri;
currContext.Items[_refererKey] = currContext.Request.UrlReferrer != null ?
currContext.Request.UrlReferrer.AbsoluteUri : null;
currContext.Items[_userAgentKey] = currContext.Request.UserAgent;
}
private void WebAppli_PostAuthenticateRequest(object sender, EventArgs e)
{
var currContext = HttpContext.Current;
// log4net doc states that %identity is "extremely slow":
// http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
// So here is some custom retrieval logic for it, so bad, especialy since I
// tend to think this is a missed copy/paste in that documentation.
// Indeed, we can find by inspection in default properties fetch by log4net a
// log4net:Identity property with the data, but it looks undocumented...
currContext.Items[_userNameKey] = currContext.User.Identity.Name;
}
}
// General idea coming from
// http://piers7.blogspot.fr/2005/12/log4net-context-problems-with-aspnet.html
// We can not use log4net ThreadContext or LogicalThreadContext with asp.net, since
// asp.net may switch thread while serving a request, and reset the call context
// in the process.
public class HttpContextValueProvider : IFixingRequired
{
private string _contextKey;
public HttpContextValueProvider(string contextKey)
{
_contextKey = contextKey;
}
public override string ToString()
{
var currContext = HttpContext.Current;
if (currContext == null)
return null;
var value = currContext.Items[_contextKey];
if (value == null)
return null;
return value.ToString();
}
object IFixingRequired.GetFixedObject()
{
return ToString();
}
}
}
Додайте його на свій веб-сайт, зразок конфіденційності IIS 7+:
<system.webServer>
<!-- other stuff removed ... -->
<modules>
<!-- other stuff removed ... -->
<add name="LogEnhancer" type="YourNameSpace.LogHttpModule, YourAssemblyName" preCondition="managedHandler" />
<!-- other stuff removed ... -->
</modules>
<!-- other stuff removed ... -->
</system.webServer>
І встановіть додатки для реєстрації цих додаткових властивостей, зразок конфігурації:
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<!-- other stuff removed ... -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message - %property%newline%exception" />
</layout>
</appender>
<appender name="SqlAppender" type="log4net.Appender.AdoNetAppender">
<!-- other stuff removed ... -->
<commandText value="INSERT INTO YourLogTable ([Date],[Thread],[Level],[Logger],[UserName],[Message],[Exception],[Ip],[Url],[Referer],[UserAgent]) VALUES (@log_date, @thread, @log_level, @logger, @userName, @message, @exception, @Ip, @Url, @Referer, @UserAgent)" />
<!-- other parameters removed ... -->
<parameter>
<parameterName value="@userName" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{userName}" />
</layout>
</parameter>
<parameter>
<parameterName value="@Ip"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Ip}" />
</layout>
</parameter>
<parameter>
<parameterName value="@Url"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Url}" />
</layout>
</parameter>
<parameter>
<parameterName value="@Referer"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Referer}" />
</layout>
</parameter>
<parameter>
<parameterName value="@UserAgent"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{UserAgent}" />
</layout>
</parameter>
</appender>
<!-- other stuff removed ... -->
</log4net>