【问题标题】:How to set log4net context property specific to an ASP.NET request?如何设置特定于 ASP.NET 请求的 log4net 上下文属性?
【发布时间】:2012-03-14 12:33:36
【问题描述】:

我一直在使用 log4net 来记录我们的 ASP.NET 网站的日志消息,最近我想添加有关发生错误的页面/处理程序的信息。因此,我决定将以下行添加到 Global.asax:

void Application_BeginRequest(object sender, EventArgs e)
{
    log4net.ThreadContext.Properties["page"] = HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath;
}

同样,我在我的转换模式中添加了%property{page}

<conversionPattern value="%newline%date %-5level %property{page} - %message%newline%newline%newline" />

这适用于单个请求。但后来我注意到在我的日志中页面属性可能会在 ASP.NET 请求期间发生变化。我登录了一个 ASHX 处理程序,在其处理过程中,页面属性将更改为指向 ASPX 页面的不同值。我得出的结论是,有另一个请求来到 ASP.NET,它的BeginRequest 被执行,log4net.ThreadContext 中的静态页面属性被更改为另一个值。

现在,我想维护每个请求的页面属性,以便我可以将执行页面的路径一致地记录到日志中。我试图找到答案,但我一无所获。解决此问题的推荐方法是什么?我确信这是 Web 服务器事件日志的非常基本的功能。

【问题讨论】:

    标签: asp.net properties log4net httpcontext


    【解决方案1】:

    由于 ASP.NET does not guarantee 整个页面请求将在同一个线程上处理,我更喜欢从 HttpContext.Current as log4net processes 日志事件中获取答案。

    以下GetCurrentPage 类通过覆盖其ToString 方法来实现log4net 手册所称的"Active Property Value"

    public class GetCurrentPage
    {
      public override string ToString()
      {
          if (null != HttpContext.Current)
          {
              return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath;
          }
          return string.Empty; // or "[No Page]" if you prefer
      }
    }
    

    在 log4net 的 GlobalContext 中的 Global.asax 的 Application_Start 中注册此类。

    protected void Application_Start(object sender, EventArgs e)
    {
        XmlConfigurator.Configure();
        GlobalContext.Properties["page"] = new GetCurrentPage();
    }
    

    当 log4net 写入行的 %property{page} 部分时,它将调用我们的 GetCurrentPage 类的 ToString 方法,该方法将在当前请求中查找值。

    【讨论】:

    • 这是天才。
    【解决方案2】:

    您是否尝试过使用 Application_PostAcquireRequestState 而不是本文所述的 Application_BeginRequest? How can I include SessionID in log files using log4net in ASP.NET?

    我们从来没有觉得需要将页面添加到日志中,因为我们在每个类中使用方法名称创建了我们的记录器:

    private static new readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    

    并且有这样的转换模式:

    <conversionPattern value="%date %P{user} %P{addr} [%property{SessionID}] %level %logger - %message%newline" />
    

    所以我们最终会在日志输出中看到类名。这还可以让您区分日志记录是发生在页面类文件本身还是它继承自的基类。即使代码在基类中执行,您的解决方案也将具有显示页面名称的优势。我想我们会考虑将 {page} 添加到我们的设置中。

    【讨论】:

    • 有趣的是,这行得通。 BeginRequest 是异步执行的,但 Application_PostAcquireRequestState 在页面请求开始时处理,之后同步触发所有其他页面事件,并且另一个页面请求必须等到第一个完成。
    • @CraigA 我认为您的代码行“private static new readonly log4net.ILog”中不需要“new”运算符。无法编辑和删除它,因为 SO 说“编辑必须至少 6 个字符”:)。
    【解决方案3】:

    在 ASP.NET 上下文中存储属性值 HttpContext.Current.Items.Add("yourProperty", value) 它可以从 log4net 布局中获得:

    <layout type="log4net.Layout.PatternLayout">
       <conversionPattern value="%aspnet-context{yourProperty}" />
    </layout>
    

    了解更多详情here.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-06
      • 2014-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      相关资源
      最近更新 更多