65.9K
CodeProject 正在变化。 阅读更多。
Home

充分利用事件查看器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (42投票s)

2003 年 5 月 18 日

10分钟阅读

viewsIcon

393524

downloadIcon

2765

本文介绍了事件日志记录的基础知识,然后详细介绍了如何使用自定义日志文件、事件源和事件类别来有效地记录消息。最后,提到了故障排除链接在错误消息中的用途。

引言

理想情况下,一个好的软件应用程序应该很少甚至从不出现错误条件或异常。但正如我们所知,现实并非如此。因此,当发生异常时,一个好的应用程序不仅应该记录纯粹的错误消息,还应该提供故障排除信息以及关于错误的详细信息,包括其来源和原因。

Windows 事件查看器一直是记录应用程序生成的错误消息最合适的地方。本文将介绍如何使用 .NET 事件日志记录 API 有效地记录错误信息。文章还讨论了一些有效的错误消息管理实践,例如维护本地化错误消息和故障排除超链接。我们将从事件日志记录的一些基础知识开始,如事件类型、日志文件等,然后介绍 .NET 相关的实现方面。

事件类型

可以记录的事件基本分为五种类型。每个事件都有特定的类型,错误日志记录应用程序在报告事件时会指示事件的类型。事件查看器使用此类型来确定在日志的列表视图中显示的图标。

事件类型

描述
信息 这表示一次重要的、成功的操作。例如,指示服务已启动的事件。
警告 警告事件表示并非立即重要但将来可能导致问题的麻烦。资源消耗是一个很好的警告事件示例。

Error(错误)

错误事件表示用户应了解的重大问题。错误事件通常表示功能或数据的丢失。
失败审核 失败审核事件是在审核访问尝试失败时发生的安全性事件。尝试打开文件失败(由于权限不足)是一个失败审核事件的示例。
成功审核 失败审核事件是在审核访问尝试失败时发生的安全性事件。尝试打开文件失败(由于权限不足)是一个失败审核事件的示例。

事件日志记录元素

以下是记录事件时使用的主要元素

日志文件

日志文件是所有事件条目所在的位置。事件日志记录服务使用存储在 EventLog 注册表项中的信息。 EventLog 键(如图 1 所示)包含几个称为 logfiles 的子项。注册表中的日志文件信息用于在应用程序读写事件日志时定位事件日志记录服务所需的资源。默认日志文件是 *应用程序*、*安全性* 和 *系统*。

EventLog key in the Registry

图 1:注册表中的 EventLog

应用程序和服务使用 *应用程序* 日志文件,而设备驱动程序使用 *系统* 日志文件。当打开审核时,系统会在 *安全性* 日志文件中生成成功和失败的审核事件。应用程序还可以通过向 EventLog 注册表项添加条目来创建自定义日志文件(也可以通过编程方式完成)。这些日志会以默认日志文件的形式出现在事件查看器中。为您的应用程序维护单独的日志文件是一个好习惯,因为这样可以更轻松地隔离您的应用程序生成的错误。此外,每个日志文件都是一个独立的可管理单元。例如,我们可以控制日志文件的大小,出于安全目的附加 ACL 等。

事件日志记录

有关每个事件的信息存储在事件日志的事件日志记录中。事件日志记录包含每个事件的时间、类型、源和类别信息。

事件源

事件源是记录事件的软件组件(或应用程序的模块)的名称。通常是应用程序的名称,或者应用程序的子组件的名称(如果应用程序很大)。应用程序和服务应将其名称添加到应用程序日志文件或自定义日志文件中,而设备驱动程序应将其名称添加到 *系统* 日志文件中。*安全性* 日志文件仅供系统使用。

Registry Structure: Event Source

图 2:注册表结构:事件源

每个日志文件都包含事件源的子项(如图 2 所示)。每个事件源包含特定于记录事件的软件的信息。下表显示了可以为事件源配置的各种注册表项

注册表值 描述
CategoryCount 指定支持的事件类别数量。
CategoryMessageFile 指定类别消息文件的路径。类别消息文件包含描述类别的语言相关字符串。
DisplayNameFile 指定存储事件日志本地化名称的文件。指定文件中存储的名称将作为事件查看器中的日志名称显示。如果注册表中不存在此条目,事件查看器将显示注册表子项的名称作为日志名称。
DisplayNameID 指定日志名称字符串的消息标识号。此数字指示本地化显示名称出现的哪个消息。
EventMessageFile 指定事件消息文件的路径。可以指定多个文件,每个文件用分号分隔。事件消息文件包含描述事件的语言相关字符串。
ParameterMessageFile 指定参数消息文件的路径。参数消息文件包含将插入事件描述字符串的与语言无关的字符串。
TypesSupported 指定支持类型的位掩码。

基本上,事件源可用于根据应用程序的需求对错误消息进行分类。例如,对于会计应用程序,您可以拥有诸如报表、计算、用户界面等源。

我们将在文章后面看到如何配置这些值。

事件类别

类别有助于组织事件,以便我们可以在事件查看器中进行筛选。每个事件源都可以定义自己的编号类别以及它们映射到的文本字符串。类别必须连续编号,从数字一开始。总类别数存储在事件源的 CategoryCount 键中。类别可以存储在单独的消息文件中,或存储在包含其他类型消息的文件中。我们将在“消息文件”部分详细讨论创建类别。

事件标识符

事件标识符唯一地标识特定事件。每个事件源都可以定义自己的编号事件以及它们映射的描述字符串。事件查看器将这些描述字符串显示给用户。

消息文件

消息文件是文本文件,其中包含有关应用程序要支持的各种消息和类别的信息。然后将这些文本文件编译为资源 DLL。与普通 DLL 相比,资源 DLL 体积小且速度快。这些资源 DLL 的优点是我们可以在多种语言中编写消息。通过使用这些 DLL,我们可以拥有真正本地化的应用程序和本地化的错误消息。每个事件源都应注册包含每个事件标识符、事件类别和参数的描述字符串的消息文件。这些文件在事件源的 EventMessageFileCategoryMessageFileParameterMessageFile 注册表值中进行注册。我们可以创建一个包含事件标识符、类别和参数描述的单个消息文件,或者创建三个单独的消息文件。多个应用程序可以共享同一个消息文件。

实现

到目前为止,本文讨论了事件日志记录背后的概念。现在,让我们考虑 .NET 相关的实现方面。

创建事件日志文件

有两种创建事件日志文件的方法

  1. 手动创建注册表项

    可以通过在 HKEY_LOCAL_MACHINE\SYSTEM\Services\EventLog 下添加条目来完成。

  2. 以编程方式

    要创建日志文件、事件源以及将事件记录到事件日志中,我们使用 System.Diagnostics 命名空间中定义的 EventLog 类。

    'Create the source, if it does not already exist. 
    If Not EventLog.SourceExists("MySource", "MyServer") Then 
        EventLog.CreateEventSource("MySource", "MyApp", "MyServer") 
    End If

    在上面代码片段中,我们在服务器 *MyServer* 上的 *MyApp* 日志文件中创建一个名为 MySource 的事件源,如果它尚不存在。

创建事件源

事件源可以手动创建,也可以以编程方式创建(如前所示),与 *事件* 日志文件类似。如果您手动创建源,则必须在源下创建多个项,例如 CategoryMessageFileEventMessageFile。下面提供的快照显示了日志文件和事件源创建后的注册表项。

Event Source Registry Entries

图 3:事件源注册表项

在图 3 中,我们为应用程序 *MyApp* 创建了一个日志文件。在此日志文件下,有两个源 *MySource1* 和 *MySource2*。在上面的示例中,*temp* 目录中的 *Msg.dll* 用于获取消息和类别名称(如 CategoryMessageFileEventMessageFile 条目所示)。该事件源有三个类别(CategoryCount 设置为 3),并支持所有事件类型(TypesSupported 设置为 7)。

消息文件

消息文本文件语法的详细解释超出了本文档的范围。

下面是一个示例消息文本文件

;//**************Category Definitions************
MessageId=1
Facility=Application
Severity=Success
SymbolicName=CAT1
Language=English
MyCategory1
.
MessageId=2
Facility=Application
Severity=Success
SymbolicName=CAT2
Language=English
MyCategory2
.

;//***********Event Definitions**************
MessageId=1000
Severity=Success
SymbolicName=MSG1
Language=English
My Error Message
.
MessageId=2000
Severity=Success
SymbolicName=GENERIC
Language=English
%1
.

注意:消息文件可以是 Unicode 格式,以支持任何语言编写的消息。在上面的示例中,消息用英语编写。类别和消息也已包含在同一文件中。

创建消息 DLL

我们需要将消息文件编译为仅资源 DLL。以下步骤解释了转换过程

  1. 创建一个 *.mc* 文件来定义消息资源表。此文件包含应用程序的所有错误消息和类别。使用消息编译器从 *.mc* 文件创建 *.rc* 和 *.bin* 文件。
    mc filename.mc.
  2. 使用资源编译器创建一个 *.res* 文件。
    rc -r -fo filename.res filename.rc 
  3. 使用链接器创建一个 *.dll* 文件。
    link -dll -noentry -out:filename.dll filename.res 

记录错误

要将事件记录到事件日志中,可以使用 EventLog 类的 WriteEntry 方法。下面是一个代码片段,详细说明了如何记录消息

Dim objEventLog As New EventLog()

'Register the App as an Event Source
If Not objEventLog.SourceExists("MySource1") Then
      objEventLog.CreateEventSource("MySource1","MyApp")
End If
objEventLog.Source = "MySource1"
objEventLog.WriteEntry("", EventLogEntryType.Error, 1000, CShort(1))

有关上述代码示例,请参阅前面显示的消息文本文件。

在此注意几点。我们将 EventID 参数传递为 1000。这对应于消息文本文件中的消息“My Error Message”。这将在事件日志中显示为消息。此外,我们将 CategoryID 传递为 1。这对应于 *MyCategory1* 类别。请注意,我没有传递任何错误消息,因为它是从消息文件中提取的。图 4 说明了执行上述代码片段后事件日志条目的外观

图 4:带有 EventIDCategory 的事件日志条目

现在我们在事件查看器中有了自己的节点。通过查看事件日志,用户可以获得大量信息,如错误的来源及其类别。不仅如此,这还使得过滤消息更加容易,因为我们现在有了自己的源和类别。此外,通过使用相同的方法,我们有效地将错误消息与应用程序代码本身分开。这将有助于我们在应用程序之间保持统一的错误消息标准,并使我们离本地化又近了一步。

在错误消息中提供链接

事件查看器提供的另一个很棒的功能是在错误消息中设置超链接,这些超链接可以链接到详细说明错误的网页。这将有助于最终用户理解和排除故障。例如,下面给出的消息有一个指向网页的链接

图 5:带有错误消息的故障排除链接

单击此链接后,我们将弹出一个对话框,如下所示

图 6:将错误信息发布到指定位置

当用户确认发送此信息时,可以编写网页来接收此信息,并根据从客户端发送的 EventIDCategorySource 向用户显示故障排除信息(此信息作为查询字符串参数发送)。

结论

事件查看器肯定提供了丰富的特性,可以通过这些特性有效地记录和跟踪错误消息。通过利用这些特性,我们可以帮助最终用户更好地理解错误及其来源,这反过来可以简化支持组的工作。

关于源文件的说明

本文附带了一个简单的 VB.NET 项目,仅展示了如何使用 EventLog 类的 WriteEntry 方法。zip 文件还包含一个 *.reg* 文件,应运行该文件以创建自定义日志文件并注册事件源。请注意,*.reg* 文件中硬编码了一些路径。因此,请相应地进行更改。最后,包含了一个批处理文件 *Compile.bat*,用于编译 *.mc* 文件以创建消息 DLL。

© . All rights reserved.