ASP.NET 健康监控






4.50/5 (6投票s)
ASP.NET 健康监控
ASP.NET 健康监控是 ASP.NET 开发人员或 Web 服务器管理员经常忘记的框架瑰宝之一。它提供了出色的监控功能,通常允许您快速诊断失败的应用程序或系统。您是否曾经想过为什么 ASP.NET 错误会出现在系统事件日志中?这要归功于 ASP.NET 健康监控及其在 master web.config 文件(存储在框架安装目录中的文件)中的配置
<healthMonitoring>
<bufferModes>...</bufferModes>
<providers>...</providers>
<eventMappings>...</eventMappings>
<profiles>...</profiles>
<rules>
<add name="All Errors Default"
eventName="All Errors" provider="EventLogProvider"
profile="Default" minInstances="1"
maxLimit="Infinite" minInterval="00:01:00"
custom="" />
<add name="Failure Audits Default" eventName="Failure Audits"
provider="EventLogProvider" profile="Default" minInstances="1"
maxLimit="Infinite" minInterval="00:01:00" custom="" />
</rules>
</healthMonitoring>
让我们快速分析一下上面的片段。一个 rule
定义了哪些 ASP.NET 健康事件将被选定的 provider
记录 (eventname
),以及记录的频率。ASP.NET 健康事件层次结构可以在 这里找到。 rule
定义中使用的 eventname
是在 web.config 文件中较早定义的映射的名称。示例事件映射如下所示
<eventMappings>
<add name="All Errors" type="System.Web.Management.WebBaseErrorEvent,
System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
startEventCode="0" endEventCode="2147483647" />
</eventMappings>
如果您回想起事件层次结构图,您会注意到 WebBaseErrorEvent
是 WebErrorEvent
和 WebRequestErrorEvent
的父级。因此,通过在规则的 eventname
属性的值中使用“所有错误”映射,我们通知 ASP.NET 框架,我们要记录所有 ASP.NET 错误事件。这是一个非常棒的功能,您可以使用它来创建自己的事件分组。其他与性能相关的事件包括
<add name="Heartbeats" type="System.Web.Management.WebHeartbeatEvent,
System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
startEventCode="0" endEventCode="2147483647" />
<add name="Application Lifetime Events"
type="System.Web.Management.WebApplicationLifetimeEvent,System.Web,Version=4.0.0.0,
Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
startEventCode="0" endEventCode="2147483647" />
<add name="Request Processing Events" type="System.Web.Management.WebRequestEvent,
System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
startEventCode="0" endEventCode="2147483647" />
如果您也对安全审计感兴趣,请查看 WebAuditEvent 规范。让我们看看这在实际的 ASP.NET 代码中是如何工作的。我们将从一个简单的 Default.aspx 页面开始
<%@ Page Language="C#" AutoEventWireup="true" %>
<script runat="server">
public void Page_Load() {
Response.Write("OK");
}
</script>
以及一个随后的 web.config 文件
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0">
</compilation>
<healthMonitoring enabled="true" heartbeatInterval="120">
<rules>
<add name="Heartbeats Default" eventName="Heartbeats" provider="EventLogProvider"
profile="Default" />
<add name="Application Lifetime Events Default"
eventName="Application Lifetime Events"
provider="EventLogProvider"
profile="Default" />
<add name="Request Processing Events Default"
eventName="Request Processing Events"
provider="EventLogProvider"
profile="Default" />
</rules>
</healthMonitoring>
</system.web>
</configuration>
如果您将此代码部署到服务器并调用 Default.aspx 页面,则新事件应出现在事件日志中。代码为 1001
和 1002
的事件提供有关应用程序启动和关闭的信息。每 120 秒(heartbeatInterval="120"
)发出的代码为 1005 的事件揭示了应用程序状态
看看这个简单的事件暴露了多少信息。例如,我们可以轻松地说出我们的应用程序是否消耗了太多内存,或者在为用户请求提供服务时是否存在任何延迟(Requests queued
)。如果发现任何问题,我们可以进一步分析出现问题的机器上的应用程序(Machine name, Process ID
)。例如,如果您的应用程序与其他应用程序共享一个应用程序池,则可能需要检查内存问题是否不是由其中一个应用程序引起的。
在本段中,我将选择几个事件,向您展示 ASP.NET 框架的哪一部分触发了它们。我通过简单地在 System.Web.Management.EventLogWebEventProvider.ProcessEvent
方法上放置一个断点来发现这一点。一旦命中断点,我就从调试器中收集了一个托管调用堆栈。例如,WebApplicationLifetimeEvent
的调用堆栈显示,当应用程序启动以处理第一个请求时,是 HttpApplicationFactory
触发了事件
System.Web.dll!System.Web.Management.EventLogWebEventProvider.ProcessEvent
(System.Web.Management.WebBaseEvent eventRaised =
{System.Web.Management.WebApplicationLifetimeEvent}) + 0x13 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseInternal(System.Web.Management.WebBaseEvent
eventRaised = {System.Web.Management.WebApplicationLifetimeEvent}, System.Collections.ArrayList
firingRuleInfos = Count = Cannot evaluate expression because the code
of the current method is optimized.,
int index0, int index1) + 0x265 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseSystemEventInternal
(string message, object source,
int eventCode, int eventDetailCode, System.Exception exception, string nameToAuthenticate) +
0xef bytes
System.Web.dll!System.Web.HttpApplicationFactory.EnsureAppStartCalled
(System.Web.HttpContext context = {System.Web.HttpContext}) + 0xaf bytes
System.Web.dll!System.Web.HttpApplicationFactory.GetApplicationInstance
(System.Web.HttpContext context = {System.Web.HttpContext}) + 0x5d bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal
(System.Web.HttpWorkerRequest wr = {Microsoft.VisualStudio.WebHost.Request}) + 0x20e bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand
(System.Web.HttpWorkerRequest wr) + 0x6e bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequest(System.Web.HttpWorkerRequest wr) + 0x47 bytes
从 WebHeartbeatEvent
调用堆栈中,我们可以读到它是由计时器(在专用的系统线程上)触发的
System.Web.dll!System.Web.Management.EventLogWebEventProvider.ProcessEvent
(System.Web.Management.WebBaseEvent eventRaised =
{System.Web.Management.WebHeartbeatEvent}) + 0x13 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseInternal
(System.Web.Management.WebBaseEvent eventRaised =
{System.Web.Management.WebHeartbeatEvent},
System.Collections.ArrayList firingRuleInfos = Count =
Cannot evaluate expression because the code of the current method is optimized.,
int index0, int index1) + 0x265 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseSystemEventInternal(string message,
object source, int eventCode, int eventDetailCode,
System.Exception exception, string nameToAuthenticate) + 0xef bytes
System.Web.dll!System.Web.Management.HealthMonitoringManager.HeartbeatCallback(object state) +
0x27 bytes
mscorlib.dll!System.Threading.TimerQueueTimer.CallCallbackInContext(object state) + 0x2c bytes
mscorlib.dll!System.Threading.ExecutionContext.RunInternal
(System.Threading.ExecutionContext executionContext,
System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0xa7 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext,
System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x16 bytes
mscorlib.dll!System.Threading.TimerQueueTimer.CallCallback() + 0x71 bytes
mscorlib.dll!System.Threading.TimerQueueTimer.Fire() + 0xc0 bytes
mscorlib.dll!System.Threading.TimerQueue.FireNextTimers() + 0x131 bytes
mscorlib.dll!System.Threading.TimerQueue.AppDomainTimerCallback() + 0x1c bytes
最后,WebRequestErrorEvent
在请求处理完成后由 HttpRuntime
触发
System.Web.dll!System.Web.Management.EventLogWebEventProvider.ProcessEvent
(System.Web.Management.WebBaseEvent eventRaised =
{System.Web.Management.WebRequestErrorEvent}) + 0x13 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseInternal(System.Web.Management.WebBaseEvent
eventRaised = {System.Web.Management.WebRequestErrorEvent},
System.Collections.ArrayList firingRuleInfos
= Count = Cannot evaluate expression because the code of the current method is optimized.,
int index0, int index1) + 0x265 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseSystemEventInternal
(string message, object source,
int eventCode, int eventDetailCode, System.Exception exception,
string nameToAuthenticate) + 0xef bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseRuntimeError
(System.Exception e, object source) + 0xa3 bytes
System.Web.dll!System.Web.HttpResponse.ReportRuntimeError
(System.Exception e = {Cannot evaluate expression because
the code of the current method is optimized.},
bool canThrow = true, bool localExecute = false) + 0x58 bytes
System.Web.dll!System.Web.HttpRuntime.FinishRequest
(System.Web.HttpWorkerRequest wr = {Microsoft.VisualStudio.WebHost.Request},
System.Web.HttpContext context = {System.Web.HttpContext}, System.Exception e) + 0x16e bytes
System.Web.dll!System.Web.HttpRuntime.OnHandlerCompletion(System.IAsyncResult ar) + 0x9c bytes
System.Web.dll!System.Web.HttpAsyncResult.Complete(bool synchronous, object result,
System.Exception error, System.Web.RequestNotificationStatus status) + 0x3a bytes
System.Web.dll!System.Web.HttpApplication.ApplicationStepManager.ResumeSteps
(System.Exception error) + 0x288 bytes
System.Web.dll!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(
System.Web.HttpContext context, System.AsyncCallback cb, object extraData) + 0xf8 bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest wr =
{Microsoft.VisualStudio.WebHost.Request}) + 0x284 bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest wr) +
0x6e bytes
System.Web.dll!System.Web.HttpRuntime.ProcessRequest(System.Web.HttpWorkerRequest wr) + 0x47 bytes
您现在可能会问自己,这种监控对您的应用程序性能有何影响。不幸的是,我无法给您确切的数字,但我向您保证,微软已尽最大努力使其尽可能不可见 。我认为最重要的性能相关决定是选择将收集健康事件的提供程序。开箱即用有几个提供程序,包括
EventLogWebEventProvider
、IisTraceWebEventProvider
、TraceWebEventProvider
或 SqlWebEventProvider
。例如,我建议使用 SqlWebEventProvider
来存储不经常发生的事件,例如审计、错误或应用程序生命周期事件 - 将它们保存在 SQL Server 表中可能有助于您将来生成一些与性能相关的报告。对于更频繁发生的事件(例如心跳或Web 请求事件),您应该寻找能够以微不足道的延迟消耗大量数据的提供程序 - 我认为 TraceWebEventProvider
、IisTraceWebEventProvider
甚至 EventLogWebEventProvider
将在这里占据主导地位。
总而言之,健康监控事件提供了对应用程序生命周期的极好见解。通过正确处理它们,您可以轻松监控您的服务器 Web 场并在它们成为主要问题之前发现任何异常。如果您想使用这项技术,请获取 我的示例应用程序并根据您的需要进行调整。