云诊断
控制 Windows Azure 中的日志记录与跟踪
Mike Kelly
与许多程序员一样,我在刚刚开始编写代码时,使用 print 语句进行调试。我不知道如何使用调试器,而 print 语句虽然简陋,却可以作为一种有效的方式用来查看程序在运行时的情况。后来,我学会了使用真正的调试器,而不再使用 print 语句作为调试工具。
很快,我开始编写在服务器上运行的代码。我发现这些 print 语句现在被用于更为复杂的“日志记录与跟踪”,而日志记录与跟踪是任何编写服务器应用程序的程序员的基本技术。
即使您能够将调试器与生产服务器应用程序相连(这通常无法实现,因为用来托管应用程序的计算机上存在一定的安全限制),也很难通过传统的调试器来揭示服务器应用程序所遇到的问题类型。许多服务器应用程序都是分布式的,运行在多台计算机上,因此对一台计算机上发生的情况进行调试,并不总是能诊断出真正的问题。
此外,服务器应用程序经常是在某位操作人员的控制下运行,操作人员不了解如何使用传统的调试器,而每次遇到问题都找来开发人员并不可取或者不切实际。
在本文中,我将介绍一些用于服务器应用程序的基本的日志记录、跟踪和调试技术。然后,将深入介绍如何在 Windows Azure 项目中利用这些技术。在此期间,您会看到如何对一些实际的应用程序执行日志记录和跟踪,并且我会介绍如何使用 Windows PowerShell 来管理针对运行中的服务的诊断。
日志记录策略
理想情况下,任何服务器应用程序(几乎所有 Web 应用程序,包括运行在 Windows Azure 下的应用程序)都应该在一开始就设计好日志记录和跟踪策略。日志记录信息应该足够可靠,能够描述每个组件中发生的几乎每一件事。但是,就像我最早在程序中添加的那些 print 语句会产生大量输出一样,日志记录也会产生大量输出。因此,设计优良的日志记录和跟踪需要包含一些方法,用来调整任意组件的日志记录的类型和容量。这就使操作人员和开发人员能够关注某个表现不正常的组件,甚至可能是某台计算机,以便获得更详细的信息来准确了解其中发生的情况,而不会在日志中生成大量无用信息,这些信息会分散注意力,甚至可能大大降低应用程序的性能。
此外,由于服务器应用程序通常都是分布式应用程序,因此必须从多台计算机(可能处于不同的应用程序角色)收集信息并进行汇总,以便全面了解发生特定问题时的情况。因此,非常重要的一点是,利用某种方法来识别通过计算机的事务线程,这样就可以汇总相关的事实信息。
Windows Azure 中提供的日志记录在社区技术预览 (CTP) 发行期间已经成熟。早期的日志记录并不比 print 语句复杂很多,可作为 Windows Azure 表存储空间中的文本来捕获。从 PDC09 版开始,Windows Azure 开始提供一套功能更加全面的日志记录与跟踪基础结构,这套基础结构基于 Windows 事件跟踪 (ETW) 框架。
此 ETW 框架在 ASP.NET 中是通过 System.Diagnostics 命名空间中的类来支持的。Microsoft.WindowsAzure.Diagnostics 命名空间继承并扩展了标准的 System.Diagnostics 类,从而能够使用 System.Diagnostics 作为 Windows Azure 环境中的日志记录框架。图 1 显示了 Windows Azure 诊断如何实现 ETW。
图 1 Windows Azure 诊断的高度概览
ETW 提供了一个模型,在此模型中可以将代码记录到一个或多个 TraceSource。每个源中允许的日志记录级别是由 SourceSwitch 控制的。这些源将依次连接到一个或多个使用者,使用者会通过各种方法来永久保存日志记录信息。
Windows Azure 提供了一种标准的使用者或侦听器,用来将您生成的日志记录信息永久保存到 Windows Azure 表存储空间或 Blob 存储空间。如果您希望利用事件数据来完成其他的任务,则可以自行编写使用者,或者也可以使用现成的使用者(但有些使用者必须经过修改才能在 Windows Azure 环境中使用)。
ETW 框架为每个事件都关联了一个 TraceEventType,如图 2 所示。前五个严重程度行是最常用的值,它们指出了跟踪输出的相对重要程度。请注意,Windows Communication Foundation (WCF) 将使用“暂停”、“继续”和“转移”这几个类型。
图 2 跟踪事件类型
跟踪事件类型 | 值 | 含义 |
关键 | 0x0001 | 严重错误或应用程序崩溃 |
错误 | 0x0002 | 可修复的错误 |
警告 | 0x0004 | 不严重的问题,但可能意味着会发生更严重的问题 |
信息 | 0x0008 | 提示性信息 |
详细 | 0x0010 | 调试跟踪(例如详细的执行流程信息、参数等等) |
开始 | 0x0100 | 开始某项逻辑操作 |
停止 | 0x0200 | 停止某项逻辑操作 |
暂停 | 0x0400 | 暂停某项逻辑操作 |
继续 | 0x0800 | 继续某项逻辑操作 |
转移 | 0x1000 | 转移到新活动 |
如果只想查找真正的问题,您可能需要确保捕获“严重”值,或许还包括“错误”值。如果想详细了解发生的情况,则需要查看“详细”以上级别的所有内容。
您的日志记录策略应该对层次结构中各值使用一致的事件类型和详细日志记录项。如果在您的应用程序中启用了对所有重点值的日志记录,应该有可能真正追踪应用程序的执行流程。这对于在生产环境中解决错误或问题具有重大价值。
您可以将侦听器、源和开关连接起来,从而通过编程来实现不同级别的输出,但这通常是通过配置文件来完成的。您可以在 app.config(对于 Windows Azure 工作者角色)或 web.config(对于 Windows Azure Web 角色)中配置输出。但如果将此配置放入 ServiceConfiguration.cscfg 中,您就可以在 Windows Azure 服务运行期间调整日志记录与跟踪选项,而不用为正在运行的代码重新部署任何更新甚至停止服务。我会在下文中对此做出详细解释。Windows Azure 还提供了 RESTful 接口,用于远程控制某些日志记录选项。Windows PowerShell 可以使用该 RESTful 接口。
日志记录、跟踪和调试输出
日志记录、跟踪和调试输出这几个术语有时可以互换使用。在本文中,我会将您的代码中通常称为诊断输出 的内容分为四种不同的类型。这四种类型大致是按从最详细到最简略的顺序排列。
- 调试输出:此类信息仅出现在应用程序的调试版中;而对于发行版,在编译时需要排除此类信息(根据编译时是否定义了 DEBUG 预处理器符号进行排除,默认情况下 Visual Studio 只会在调试版中定义此符号)。通常,调试输出包括类似于您添加的“声明”之类的信息,这些信息有助于发现代码不符合预期前提条件、会导致错误甚至数据结构转储的情况。添加这些声明可帮助您在调试和测试过程中调试算法。
- 跟踪:此类信息是指一些语句,用于在程序运行时帮助跟踪控制流和程序状态。 想象一下:运行一个调试器,一步步执行代码,然后在监视窗口中查看关键变量的值。跟踪语句的作用就是在您无法连接调试器时再现这种体验。从理论上说,跟踪语句应该能够提供足够的上下文信息,通过阅读跟踪语句,您可以了解应用程序在每个控制点都选择哪条执行路径,以及代码中接下来的操作类型。如果在编译时定义了 TRACE 预处理器符号,就会启用跟踪,在发行版和调试版中都可以进行跟踪。(默认情况下,Visual Studio 在调试版和发行版中均定义了 TRACE,当然,您可以进行更改。)
- 事件日志记录:此类信息是指一些语句,用于在应用程序运行过程中捕获主要事件,例如:开始事务,或者向数据库中添加项目。事件日志记录与跟踪的差别在于它捕获的是重要状态,而不是详细的控制流。
- 错误日志记录:此类信息是指一些特殊的事件日志记录,用于捕获异常或可能存在危险的情况。具体的例子包括:捕获的任何异常;您应该能够访问另一台计算机上的资源却不能访问的情况(当然,您的应用程序应该能够妥善处理,但也需要注意);以及您认为不会出错却从 API 返回错误的情况。
在尚未出现错误但有征兆表明很快会出现错误的情况下(例如,配额接近上限,或者某项事务成功执行但花费的时间超出正常情况),错误日志记录对操作人员也很有用。这些类型的日志记录事件可帮助操作人员在问题发生之前主动采取措施,使应用程序避免发生停机。
大多数优秀的开发人员都习惯于在应用程序中包含调试输出,以帮助他们在开发过程中诊断问题,而且许多人已经开发出了用于错误日志记录的某种解决方案。
但是,您需要确保不仅考虑到调试输出和错误日志记录选项,还拥有一种可靠的跟踪和事件日志记录策略,从而真正有助于诊断只有在生产环境中的高压负载下才会发生的问题。
此外,还需要慎重考虑,您现在当作调试输出的许多内容是否确实需要跟踪以及在发行版和调试版中均提供。生产环境中表现不正常的应用程序通常运行的是发行版。如果跟踪语句已存在,但是被禁用了(我会在下文中介绍如何操作),您可以有选择地启用它们,以便从发行版获得类似于调试版的详细输出,从而帮助您诊断问题。
Windows Azure 中的跟踪与日志记录
您可以直接使用 Windows Azure 的日志记录功能,该功能是 Windows Azure SDK 的一部分。使用 Logger.NET、Enterprise Library、log4net 或 Ukadc.Diagnostics 之类的日志记录框架有一些好处。这些框架为您的日志记录消息附加了结构,也有助于提供上文所述的某些可配置性。但是,大部分框架未经调整,不能在 Windows Azure 环境中使用;有些也远不止是日志记录框架。
对于本文的示例代码,我决定只使用标准的 Windows Azure 日志记录与跟踪 API,并在其上加上一个薄层来提供动态配置功能。您可能会发现,如果在此基础上为您的日志记录与跟踪策略构建一些帮助程序类和一个框架,会对您有所帮助,或者也可以留意其他框架是否具有适用于 Windows Azure 的版本。
使用 Visual Studio 在 Windows Azure 中创建新服务后,就已经在该服务中启用基本的日志记录功能。通过 Windows Azure 模板生成的样板工作者角色和 Web 角色的代码已经配置并启用了诊断侦听器。
若要在 Windows Azure 服务中启用简单的日志记录,请在 Visual Studio 中使用“Windows Azure 云服务 (Visual C#)”模板来启动新项目。然后为项目指定名称。在我的示例中,我使用了 MSDNSampleLoggingService。单击“确定”。
“新建云服务项目”向导将运行。在该向导中,为项目添加一个工作者角色和一个 Web 角色。将工作者角色重命名为 LoggingWorkerRole,将 Web 角色重命名为 LoggingWebRole,然后单击“确定”。Visual Studio 将会创建该项目。
此时,您可以浏览生成的代码。查看 LoggingWorkerRole 项目中的 app.config,您会注意到出现了以下代码:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="false" indentsize="4">
<listeners>
<add name="AzureDiagnostics" type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Mi-crosoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
此代码会将标准 Windows Azure 诊断侦听器连接到您的代码,这就意味着,除非您进行更改,否则您从工作者角色执行的任何日志记录和跟踪都会被定向到 Windows Azure 侦听器 (DiagnosticMonitorTraceListener)。对于为此服务创建的 Web 角色,您会在 web.config 中找到类似的条目。
如果您查看工作者角色项目中的 WorkerRole.cs 文件,还会在 OnStart 方法中看到以下行:
DiagnosticMonitor.Start("DiagnosticsConnectionString");
并且在 Run 方法中,您会看到对跟踪的调用:
// This is a sample worker implementation. Replace with your logic.
Trace.WriteLine("LoggingWorkerRole entry point called", "Information");
最后,如果您查看位于“服务”根目录下的 ServiceConfiguration.cscfg 文件,您会看到对应于工作者角色和 Web 角色的以下行:
<Setting name="DiagnosticsConnectionString"
value="UseDevelopmentStorage=true" />
该行用于告知 Windows Azure 侦听器,使用哪个存储帐户来永久保存日志记录与跟踪信息。在本例中,日志记录信息将存储在本地计算机上的开发存储空间中。如果将此设置切换为 Windows Azure 云存储帐户,则可以将日志定位到云存储空间。下面是本文所提供的示例代码中的格式示例:
<Setting name="DiagnosticsConnectionString"
value="DefaultEndpointsProtocol=https;AccountName=Xxxxxx;AccountKey=Yyyyyy" />
AccountName 和 AccountKey 值需要根据您的实际 Azure 帐户和密钥来自定义。您可以从您服务的存储帐户门户(网址为 windows.azure.com)获取此信息。AccountName 是 URL 中对应于表和 Blob 存储端点的第一部分(“.table.core.windows.net”之前的部分)。AccountKey 是您的存储帐户的 base-64 编码的主访问密钥。
请注意,由于有一个单独的存储帐户用于诊断,因此您可以选择将诊断信息与其他的应用程序数据分开存放。为此,请在门户页面上单击“新建服务”,选择“存储帐户”,然后指定名称(例如 MyAppLogs),从而设置单独的存储帐户。您可能需要设置一个相关性组,使您的日志与服务存储在相同的区域中。
至此,您已经快速了解了 Windows Azure 服务中的跟踪代码,可以运行您创建的简单 Web 角色项目了。请注意,在调试版中,Windows Azure 所提供的默认侦听器还会将输出定位到 Visual Studio 中的输出窗口,因此您会在调试窗口中看到 OnStart 消息:
Information: LoggingWorkerRole entry point called
如果您想在服务运行后查看日志,该怎么办?默认情况下,Windows Azure 不会将日志永久保存到存储空间。您可以在自己角色的 OnStart 方法中添加几行代码,命令 Windows Azure 这么做:
TimeSpan tsOneMinute = TimeSpan.FromMinutes(1);
DiagnosticMonitorConfiguration dmc =
DiagnosticMonitor.GetDefaultInitialConfiguration();
// Transfer logs to storage every minute
dmc.Logs.ScheduledTransferPeriod = tsOneMinute;
// Transfer verbose, critical, etc. logs
dmc.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
// Start up the diagnostic manager with the given configuration
DiagnosticMonitor.Start("DiagnosticsConnectionString", dmc);
如果将此代码添加到 WorkerRole.cs 中并重新运行,会使 Windows Azure 每分钟向开发存储空间转移一次日志。您也可以选择根据需要转移日志(请参见我的示例应用程序中 admin.aspx.cs 文件的代码来了解如何操作),或者使用下文介绍的 Windows PowerShell 命令。请记住,一旦将日志转移到存储空间,您就需要为所使用的存储空间付费,而且当您不再需要这些信息时,需要自行将其删除。
当您将日志传送到 Windows Azure 存储空间后,还需要一个工具来查看存储表,从中可以看到日志。我使用了 Cerebrata 的 Cloud Storage Studio (cerebrata.com)。Cerebrata 曾经发布过一款名为 Azure Diagnostics Manager 的工具,CodePlex (codeplex.com) 上也提供了用于查看云存储空间和诊断信息的免费工具。日志存放在名为 WADLogsTable 的表中,您可以在图 3 中看到该表。
图 3 永久保存在开发存储空间中的日志
当您查看存储空间中的日志时,会注意到两件事。首先,Windows Azure 会自动将一些信息与每个记录的事件相关联,例如时间戳、滴答计数(以 100 纳秒为单位提供更详细的时间信息),以及有关部署、角色和角色实例的信息。这使您能够根据需要将日志限定在特定的实例范围内。
其次,每个事件都关联了 Level 和 EventId。Level 对应于图 2 中的值:记录为“信息”的跟踪事件的 Level 值为 4,记录为“错误”的跟踪事件的 Level 值为 2。通过 Trace.WriteLine 发送(如样板代码所执行的操作)的一般事件的 Level 值为 5(详细)。
EventId 的值由您指定。上文所示的基本 Trace.WriteLine 调用并没有让您指定该值;您必须使用其他跟踪方法来传递 EventId。
有选择地启用跟踪与日志记录
典型的应用程序通常由多个逻辑组件组成。例如,您可能有一个数据库组件,用来处理 Windows Azure 存储空间中的数据模型。您的 Web 角色可能被依次划分到一个管理组件和一个用户组件中(根据应用程序的需要,甚至可能进一步划分到逻辑组件中)。
您可以将日志记录与跟踪选项(启用哪种日志记录,详细程度如何)与这些组件相关联。这样,您就可以有选择性地仅在需要的组件中启用跟踪,从而避免混乱。
此处的关键方法是不直接调用 Trace,而是使用多个 TraceSource 实例,通常情况下,对每个命名空间使用一个。TraceSource 有一个关联的 SourceSwitch,用于控制是否启用该源以及所需的输出级别。重要的是,SourceSwitch 值不是在编译时设置的,而是在运行时设置的。因此,您可以从应用程序的各个部分启用或禁用诊断输出,而不需要重新编译应用程序甚至重新部署不同的版本。
在示例代码中,WorkerDiagnostics.cs 和 WebDiagnostics.cs 包含跟踪源和开关的配置。下面摘录了一段:
// Trace sources
public TraceSource ConfigTrace;
public TraceSource WorkerTrace;
// Add additional sources here
// Corresponding trace switches to control
// level of output for each source
public SourceSwitch ConfigTraceSwitch { get; set; }
public SourceSwitch WorkerTraceSwitch { get; set; }
// Add additional switches 1:1 with trace sources here
然后,在角色的配置文件中,您可以将这些内容连接到侦听器,如图 4 所示。它首先会将标准的 Windows Azure 诊断侦听器设置为共享侦听器,使其可供 <sources> 项引用。然后会配置两个源:WorkerTrace 源和 ConfigTrace 源。此外,还会设置相应的开关,您可以用来调整输出级别。ConfigTrace 提供了最详细的输出;WorkerTrace 仅提供“错误”输出。
图 4 配置跟踪源和侦听器
<configuration>
<system.diagnostics>
<sharedListeners>
<add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="AzureDiagnostics">
<filter type="" />
</add>
</sharedListeners>
<sources>
<source name="ConfigTrace">
<listeners>
<add name="AzureDiagnostics" />
</listeners>
</source>
<source name="WorkerTrace">
<listeners>
<add name="AzureDiagnostics" />
</listeners>
</source>
</sources>
<switches>
<add name="ConfigTrace" value="Verbose"/>
<add name="WorkerTrace" value="Error"/>
</switches>
<trace>
<listeners>
<add name="AzureDiagnostics" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
您不一定要为开关指定与源相同的名称,但如果它们名称相同,事情会变得更简单。如果它们的名称不同,您需要为源元素添加 switchName 属性,以指明用来控制该源的输出的开关名称。此情况允许您在多个跟踪源之间共享一个开关。请注意,跟踪源和开关的名称都是区分大小写的,因此将名称传递给构造函数时,代码中的大小写情况必须完全匹配。
如果您想完全避免使用开关,只需在源元素中添加 switchValue 属性,为该源指定所需的开关值即可。您使用的开关值实际上是从配置文件中解析为图 5 中所定义的 SourceLevel 之一,该图还显示了您传递给 TraceSource 调用的 TraceEventType 如何与该源的 SourceLevel 设置交互来决定传递的内容。
图 5 跟踪源级别和 TraceEventType
您可能已经注意到了,SourceLevel 只是一个位掩码,它在运行时与 TraceEventType 执行 AND 运算,以决定是否记录事件。若要获取类似“警告”和“活动跟踪”这样的组合,请为位域指定数字值作为开关值,而不是指定图中所示的符号值。
除了开关以外,侦听器还可以使用 TraceFilter 来添加更复杂的运行时逻辑,从而设定是否允许特定的消息通过。编写自定义的 TraceFilter 不在本文的论述范围内,但是您在 CodePlex 上的 Ukadc.Diagnostics 项目文档中可以找到有帮助的示例 (ukadcdiagnostics.codeplex.com/wikipage?title=LoggingPrimer)。
在运行时更改记录的内容
这是 ASP.NET 跟踪的默认工作方式,也适用于部署到 Windows Azure 中的服务。问题是您可能希望能够在运行时更改开关值,但是 Windows Azure 不允许您仅仅替换 web.config 或 app.config,而不重新部署服务。ASP.NET 对此问题的一般解决方案是使用 WebConfiguationManager 来更改配置值,但是 Windows Azure 目前也不允许您对部署到云中的服务执行此操作。
可行的解决方案是在 ServiceConfiguration.cscfg 中镜像这些开关的值。Windows Azure 允许您在服务运行期间通过开发门户编辑该文件(或上载新文件)。但是,您必须另外编写一些代码来执行此工作。
默认的 System.Diagnostics 代码只知道 app.config 或 web.config 中的设置,但是通过 RoleEnvironmentChanging 和 RoleEnvironmentChanged 事件,您的角色将获得有关 ServiceConfiguration.cscfg 中变更的运行时通知。然后您可以决定是重新使用(重新启动)该角色,还是仅仅更新配置值。后一种处理方式是您需要对跟踪开关采取的处理方式。重新启动角色可能会使间歇性问题不再出现。本文的示例代码演示了如何执行此操作:在 ServiceConfiguration.cscfg 中添加一对值(请注意,您还必须编辑 ServiceDefinition.csdef,因为该文件提供了架构),并在您的角色中添加一些代码。
在开发构造上进行测试
在开发结构上进行测试会如何?在开发结构上,您不需要 Windows Azure 门户就可以编辑配置(如同您对部署到云中的服务所执行的操作)。首先,确定 Windows Azure 分配给正在运行的开发结构服务的部署 ID。在服务运行期间,您可以从系统任务栏显示出开发结构 UI,从而查看该 ID。该值应该是类似 177 的数字。
- 转至在生成过程中您的服务二进制文件所存放的目录,这通常是您的服务代码下的 \bin\debug 或 \bin\release 目录。从中会找到您在生成应用程序时创建的 ServiceConfiguration.cscfg。
- 接下来,使用文本编辑器编辑该文件,以使用所需的跟踪开关。例如,在示例代码中,将 WebTrace 从 Off 改为 Verbose。
- 接下来,在 Windows Azure SDK 命令提示符(“开始”|“所有程序”|“Windows Azure SDK v1.1”|“Windows Azure SDK Command Prompt”)中,运行以下命令:
csrun /update:NNN;ServiceConfiguration.cscfg
NNN 是指您先前找到的 Windows Azure 开发 ID。此命令将在开发结构中更新配置设置并触发事件,对于部署到云中的服务,则可以在 Windows Azure 开发门户上单击“配置”按钮来更新配置设置并触发事件。
其他诊断信息
尽管本文重点介绍您的应用程序使用 System.Diagnostics 所记录的表格式数据,但 Windows Azure 还可以捕获 IIS 日志和失败请求跟踪(以前称为“失败请求缓冲”,简称 FREB)日志等等。其中,一些数据存放在 Windows Azure Blob 存储空间中,一些存放在 Windows Azure 表存储空间中。图 6 列出了可用的日志以及它们的存储位置。请注意,对于那些默认不启用的日志,您通常必须在 web.config 或 app.config 中做出一点更改,Windows Azure 才能收集这些日志。让我们深入探讨一个在默认情况下不会收集的示例,以便了解如何操作。
图 6 标准的 Azure 日志记录选项
日志类型 | Windows Azure 存储格式 | 默认情况下是否收集? | 备注 |
根据您的代码生成的 Windows Azure 日志 | 表格 | 4 | 必须在 web.config 或 app.config 文件中添加跟踪侦听器,如示例代码中所示。存储在 WADLogsTable 中。 |
IIS 7.0 日志 | Blob | 4 | 仅适用于 Web 角色。存储在 wad-iis-logfiles\<部署 ID>\<Web 角色名>\<角色实例>\W3SVC1 路径下的 Blob 容器中。 |
Windows 诊断基础结构日志 | 表格 | 4 | 有关诊断服务本身的信息。存储在 WADDiagnosticInfrastructureLogsTable 中。 |
失败请求日志 | Blob | 仅适用于 Web 角色。通过设置 web.config 中的 system.WebServer 设置下的跟踪选项来启用。存储在 wad-iis-failedreqlogfiles\<部署 ID>\<Web 角色名>\<角色实例>\W3SVC1 路径下的 Blob 容器中。 | |
Windows 事件日志 | 表格 | 通过在设置初始配置时更改 DiagnosticMonitor Configuration.WindowsEventLog 来启用。存储在 WADWindowsEventLogsTable 中。Steve Marx 的博客 (blog.smarx.com/posts/capturing-filtered-windows-events-with-windows-azure-diagnostics) 说明了如何使用这类日志。 | |
性能计数器 | 表格 | 通过更改 DiagnosticMonitor Configuration.PerformanceCounters 来启用。存储在 WADPerformanceCountersTable 中。示例代码的工作者角色就设置了一个性能计数器。 | |
崩溃转储 | Blob | 通过调用 CrashDumps.EnableCollection 来启用。存储在 wad-crash-dumps 路径下的 Blob 容器中。由于 ASP.NET 会处理大部分异常,因此通常情况下,此类日志仅对工作者角色有用。 | |
自定义的错误日志 | Blob | 不在本文的论述范围内,请参见 Neil Mackenzie 的博客 (nmackenzie.spaces.live.com/blog/cns!B863FF075995D18A!537.entry),其中的示例可帮助您了解如何使用此类日志。 |
例如,让我们来看看如何在您的 Web 角色上启用来自 IIS 的 FREB 日志记录。若要查看具体效果,请下载本文提供的 MSDNSampleLoggingService 示例代码。打开 LoggingWebRole 的 web.config,并找到带 <system.webServer> 标记的部分。请注意,图 7 中所示各行已添加到默认的 Windows Azure web.config 中。这样,任何超过 15 秒的请求,或状态代码在 400 到 599 之间的请求,都会引发故障日志记录(failureDefinitions 元素)。
图 7 LoggingWebRole 的失败请求日志记录
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET"
areas="Infrastructure,Module,Page,AppServices"
verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server"
areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module"
verbosity="Verbose" />
</traceAreas>
<failureDefinitions timeTaken="00:00:15" statusCodes="400-599" />
</add>
</traceFailedRequests>
</tracing>
如果您打开 LoggingWebRole 项目中的 about.aspx.cs,会注意到在 PageLoad 方法中,使用以下代码行添加了 18 秒的随机延时:
System.Threading.Thread.Sleep(18000);
这会迫使加载此页面的操作被依据先前指定的定义而当作失败的请求。
若要查看 FREB 日志,请重新生成应用程序,并将其重新部署到开发结构中,然后在任务栏的通知区域中找到开发结构控制器(它经常会由于不活动而被隐藏,因此您可能需要单击任务栏上的“显示隐藏图标”按钮)。右键单击该控制器,然后选择“显示开发结构 UI”。如果您的应用程序正在运行,这将显示应用程序的相关信息。
展开“Web 角色”,然后右键单击角色实例 (0)。选择“打开本地存储”以打开本地计算机上用来保存日志的文件夹(请参见图 8)。在该文件夹内,日志位于 \directory\DiagnosticStore 文件夹中。这是因为在示例代码中,Web 角色被配置为将诊断信息存储在开发存储空间。如果您将 DiagnosticsConnectionString 设置改为云存储帐户,则日志将永久保存在与该存储帐户关联的 Blob 存储空间。您可以使用 Cloud Storage Studio 来查看 Blob 存储容器,从中可以看到日志。
图 8 打开保存在本地开发结构存储空间的日志
管理正在运行的服务的诊断信息
尽管您可以利用日志记录来深入检查您的代码,但是通常并不希望在生产服务运行期间,将所有日志记录信息都永久保存到存储空间中。您可能只希望将错误信息和严重信息保存到永久性日志中,而阻止更详细的信息(记录为“详细”或“信息”)。
但是如果出现问题怎么办?您不希望重新部署新的服务版本,也不希望出现的问题会奇迹般地消失(您可能知道如何有效重启可以使难以捉摸的问题消失)。
相反,允许表现异常的代码继续运行,增加将保存到日志记录表或 Blob 存储空间中的信息量,是一种更有效的做法。这样更有可能在应用程序的运行过程中找出其中问题的原因所在。
在上文中,我介绍了如何针对特定 TraceSource 适当调整传递到 Windows Azure 诊断的日志记录信息详情。这是一种追溯式编辑方法,可用来控制要记录哪些信息。在本部分中,我将演示一种常规的 Windows Azure 诊断设置,该设置可确定从 TraceSource 中通过的信息如何进入永久性存储空间。
Windows PowerShell cmdlet 可以管理正在运行的 Windows Azure 服务的许多方面,包括其诊断。您可以从本地计算机运行这些 cmdlet,它们将通过 Internet 连接到正在运行服务的 Windows Azure 云服务器,从而提供信息和调整参数。Windows 7 中预装了 Windows PowerShell;对于 Windows Vista,则可以从 microsoft.com/powershell 网址下载。从 code.msdn.microsoft.com/azurecmdlets 下载 Windows Azure Service Management CmdLet,然后按照指令进行安装。图 9 中显示了与 Windows Azure 诊断相关的命令。
图 9 Windows Azure Management Diagnostics Cmdlet
名称 | 说明 |
Get-ActiveTransfers | 返回一组处于活动状态的诊断转移以及相关的转移信息。 |
Get-CommonConfigurationLogs | 获取对所有日志记录缓冲区都通用的配置值。这包括对配置变更进行轮询的时间间隔,以及分配给内存中日志的缓冲区大小。 |
Get-DiagnosticAwareRoleInstances | 返回一些角色实例的 ID 列表,这些实例处于活动状态、正在运行诊断监视器。 |
Get-DiagnosticAwareRoles | 列出一组角色,这些角色至少已成功启动一个诊断监视器。 |
Get-DiagnosticConfiguration | 针对指定的缓冲区名称(Logs、Directories、PerformanceCounters、WindowsEventLogs 或 DiagnosticInfrastructureLogs)获取缓冲区配置。 |
Set-CommonConfigurationLogs | 设置对所有日志记录缓冲区都通用的配置值。 |
Set-FileBasedLog | 针对基于文件的日志来设置缓冲区配置。 |
Set-InfrastructureLog | 针对底层 Windows Azure 诊断基础结构所生成的日志来设置缓冲区配置。诊断基础结构日志对解决诊断系统本身的问题很有帮助。 |
Set-PerformanceCounter | 针对您的服务所收集的性能计数器数据来设置缓冲区配置。 |
Set-WindowsAzureLog | 针对您的服务所生成的基本 Windows Azure 日志来设置缓冲区配置。 |
Set-WindowsEventLog | 针对您的服务所生成的 Windows 事件日志来设置缓冲区配置。 |
Start-OnDemandTransfer | 启动指定数据缓冲区的按需转移。这会将数据转移到 Windows Azure 存储空间(表或 Blob 存储空间)。 |
Stop-ActiveTransfer | 在给定转移 ID 的情况下,停止处于活动状态的按需转移。 |
例如,要找出特定角色实例的当前转移参数,需要传递部署 ID(从您部署服务的 Windows Azure 开发人员门户中),以及您在服务角色的 app.config 或 web.config 中为 DiagnosticsConnectionString 使用的存储帐户名称和密钥(请参见图 10)。请注意,Windows PowerShell 将提示缺少一对必要的参数:角色实例以及所需缓冲区的名称。Logs 是指标准的 Windows Azure 日志。结果显示过滤级别为“详细”,并且计划每分钟转移一次。
图 10 使用 Windows PowerShell 对正在运行的服务进行的诊断配置
若要更改对此角色的配置,请使用 Set-DiagnosticConfiguration cmdlet,如图 11 中所示。请注意,我已将转移周期从一分钟改为两分钟,并且将过滤级别从“详细”改为“错误”,这意味着只有记录为“错误”和“严重”的事件才会被发送到永久性存储空间。
图 11 从 Windows PowerShell 中更改诊断配置
您可以从 Windows PowerShell 远程执行的最有用的操作也许是强制立即转移日志信息。图 12 显示了如何执行此操作。
图 12 使用 Windows PowerShell 启动 IIS 日志转移操作
首先,我查询是否存在日志按需转移操作。当前的 Windows Azure 诊断实现中有限制:任何时候都只能运行一个特定类型的按需转移。看到没有任何正在进行的按需转移操作,我请求了一个操作,并传入了服务部署 ID、角色和实例、我要转移的日志类型以及数据转移的时间间隔。(对于日志类型,Directories 意味着基于文件的日志,包括 IIS 日志。Logs 表示通过 TraceSource 发送的基于 Windows Azure 表的日志。)
我还传入了通知队列名称,Windows Azure 诊断将在转移完成后从该队列中发送通知。通过试验我发现,如果我不传入通知队列,转移就像没有发生过一样。我取回了用来标识转移请求的 GUID。然后,我查询了请求状态,发现它已发布,这意味着它正在进行中。
当前的 Windows Azure Service Management CmdLets 版本在请求完成后似乎没有相应的提示,但如果您在 Blob 存储空间中查询您的诊断存储,则会看到日志很快显示在“容器”层次结构中(如果您请求转移的信息存储在表存储空间中,则应查询 Windows Azure 表存储空间)。
总结
综合运用以下两种方法,您就应该能够全面控制使用 Windows Azure 服务获得的诊断输出:调整您在代码中定义的 TraceSource 配置参数,以及使用 Windows Azure Service Management CmdLet 将信息转移到永久性存储空间中。
当然,要排除生产代码中的故障,最重要的是要在开发前期制定可靠的诊断输出策略,然后在整个程序编码过程中遵循该策略。使用 TraceSource 以及 Windows Azure 提供的工具,可配置从您的应用程序流向存储空间的诊断输出的详细程度,从而帮助您在出现问题时获得刚好能满足您需要的信息量。
最坏的情况莫过于感觉服务器上表现异常的代码就像一个黑盒子,对您来说完全不透明。本文介绍的切实有效的诊断代码和工具将帮助您打开盒盖,了解其中的真相。
Mike Kelly 是一位专注于软件开发领域的顾问,能够帮助收购的企业融入更大的企业中。他曾在 Microsoft 工作过 15 年,历任各种产品开发角色以及卓越工程管理 (Engineering Excellence) 团队的新兴实践 (Emerging Practices) 主管。您可以通过以下邮件地址与他联系:himself@mikekellyconsulting.com。
衷心感谢以下技术专家对本文进行了审阅:来自 Microsoft 的 Sumit Mehrotra、Michael Levin 和 Matthew Kerner,以及 Neil Mackenzie 和 Steven Nagy