当错误发生时,系统管理员或技术支持需要知道错误原因是什么,如何恢复丢失数据和阻止错误复现。WINDOWS的Event-logging服务为此提供了解决方案。应用程序,操作系统或其它系统服务可以向该服务记录重要的事件消息,如:磁盘空间不足、没有访问权限等。系统管理员可以通过这些消息来确定错误发生的原因以及发生的上下文环境。通过定期的查看这些日志还可以帮助管理员在系统造成大的破坏前发现问题。
Event-logging服务是随着WINDOWS系统的启动而被默认启动的,但可以通过服务控制面板停止该服务。Windows for Workgroups、Windows NT、及以后以Windows NT技术为基础的各Windows版本支持该服务,Windows 3.1、Windows 95、Windows 98和Windows CE均不对它提供本地的(Native)支持。
EVENT LOG共有五种事件类型,所有的事件必须只能拥有其中的一种事件类型。
事件类型
|
描述
|
信息
(Information)
|
信息事件指很少发生但重要的成功操作。例如, 当Microsoft® SQL Server™ 成功加载后,它可以记录一条信息日志"SQL Server has started."。注意,当每次系统启动记录事件时,适用于主要的服务器服务,不适用于普通的桌面应用程序 (如:Microsoft® Excel)。 |
警告
(Warning)
|
警告事件指不是直接的、主要的,但是会导致将来问题发生的问题。资源消费是一个产生警告信息的很好的应用。例如, 一个应用程序可以在磁盘空间小时产生一个警告事件。如果应用程序可以在不丢失功能和数据的情况下从这一事件中恢复,那么这是一个典型的警告事件。 |
错误
(Error)
|
错误事件指用户应该知道的主要的问题。错误事件通常指功能和数据的丢失。例如, 如果一个服务不能作为系统引导被加载,那么它会产生一个错误事件。 |
成功审核
(Success audit)
|
成功审核事件是安全事件,它在一个要被审核的访问,访问成功时产生。例如,成功登陆可以产生成功审核事件。 |
失败审核
(Failure audit)
|
失败审核事件是安全事件,它在一个要被审核的访问,访问失败时产生。例如,打开文件失败可以产生失败审核事件。 |
信息、警告、错误三种事件类型一般用于除系统的Security日志以外的日志文件中。而成功审核、失败审核只用于Security日志中。
Event-logging服务用到的信息都被存放在了注册表的EventLog键下。EventLog键下包含几个子键,这些子键被叫做日志文件。通常情况下会有三个子键,它们是Application, System,和Security。在某些机器上,由于安装了微软的其他应用软件会多出几个子键,常见的有Directory Service, File Replication Service和DNS Server Logs。当应用程序需要读写事件日志时,Event-logging服务用注册表中的日志文件信息来定位资源。下面是Event-logging在注册表中的结构:
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
EventLog
Application
Security
System
CustomLog
SYSTEM
CurrentControlSet
Services
EventLog
Application
Security
System
CustomLog
应用程序和服务用Application日志文件;设备驱动程序用System日志文件;当操作系统的审计功能打开后,审计成功与审计失败的事件将写入Security日志文件。应用程序也可以通过在EventLog键下再加入一个子键项来顶制一个日志文件。(注:Windows NT不支持定制日志文件)。
日志文件通常在%SystemRoot%/System32/Config子目录下:
APPEVENT.EVT:应用事件日志
SECEVENT.EVT:安全事件日志
SYSEVENT.EVT:系统事件日志
事件日志都以.evt做为扩展名,里面都是以Event的二进制格式进行的信息存放。
在注册表中每个日志文件都有自己的值,Event-logging服务通过这些值来访问与控制日志文件的相关资源。下表即日志文件的值:
名称
|
类型
|
描述
|
File
|
REG_EXPAND_SZ |
日志文件的路径名。路径名中可以包含环境变量,当包含环境变量时,需要将文件路径重新定位。如:%SystemRoot%/system32/config/
AppEvent.Evt
|
MaxSize
|
REG_DWORD | 指日志文件增长所能达到的最大物理大小。这个值的增长粒度必须是64KB(0x00010000)。如果这个值不是64K的边界值(即被64K正除),那么该值将被自动对准到下一个64K的边界值。该值默认为512KB |
PrimaryModule
|
REG_EXPAND_SZ | 运用日志文件的主要模块(事件源)的名字。该值只在日志文件是Security时使用。 |
RestrictGuestAccess
|
REG_DWORD | 客人和NULL帐号在默认情况下可以访问Application和Systeme事件日志,如果将该值设为1可以阻止它们的访问。Security事件日志总是被保护,不允许客人与NULL帐号的访问。 |
Retention
|
REG_DWORD | 该值表示事件日志保持的时间。当该值为0x00000000时表示按需要覆盖重写日志。当该值为0xffffffff时表示不允许覆盖重写日志。其他值均被解释为覆盖“N”天前的日志。天是用秒来表示的(0x00015180表示一天,0x01e13380表示365天)。该值的增长粒度为86400,当数据不规则时需要进行对齐。默认的值为0x00093a80(7天) |
Sources
|
REG_MULTI_SZ | 所有注册到日志文件下的事件源的链表。所有的事件源的名字之间以字符NULL分隔。链表最后以两个NULL终止。链表的最后一个名字是日志文件的名字,当有新的事件源加入时,进行自动调整。 |
在事件日志中的所有信息都是以事件日志记录格式(event log record)进行存放的。该结构的名称是EVENTLOGRECORD。下表是事件日志记录格式的详细描述:
名称
|
类型
|
描述
|
Length
|
DWORD | 事件记录的大小,单位字节。该长度包括在事件日志记录结尾加入的用于DWORD对齐的补丁字节。该长度最小56字节。 |
Reserved
|
DWORD | 保留 |
RecordNumber
|
DWORD | 记录的序号。这个值可以用于当标志设为EVENTLOG_SEEK_READ时,ReadEventLog函数从指定的记录开始读取。 |
TimeGenerated
|
DWORD | 条目被提交的时间。指从00:00:00 January 1, 1970(UTC)开始到当前的秒数。 |
TimeWritten
|
DWORD | 条目被Event-logging服务写如日志文件的时间。指从00:00:00 January 1, 1970(UTC)开始到当前的秒数。 |
EventID
|
DWORD | 事件标识号。该值被事件的事件源描述为事件的源名字,用它来定位事件源的消息文件中的源名字符串。 |
EventType
|
WORD | 事件的类型。在EVENT LOGGING事件类型中有介绍。 |
NumStrings
|
WORD | 日志中的字符串的个数。这些字符串在StringOffset所指的位置。它们将在显示给用户之前被组成消息。 |
EventCategory
|
WORD | 事件的分类。这个指依赖于事件源。 |
ReservedFlags
|
WORD | 保留 |
ClosingRecordNumber
|
DWORD | 保留 |
StringOffset
|
DWORD | 事件日志记录中的描述字符串的偏移。 |
UserSidLength
|
DWORD | UserSid的字节大小。当没有提供安全标识时,该值为0 |
UserSidOffset
|
DWORD | 事件日志记录中的安全标识(SID)的偏移。可以通过LookupAccountSid函数有SID获得用户名。 |
DataLength
|
DWORD | 事件日志记录中事件细节数据的字节大小。 |
DataOffset
|
DWORD | 事件日志记录中事件细节数据的偏移。 |
日志文件下的子键叫做事件(EVENT)源。每个事件源是记录事件日志的软件或软件的子控件的名字。应用程序和服务将自己的名字加在Application日志文件或定制日志文件下,设备驱动程序将自己的名字加在System日志文件下,Security日志文件由系统自己使用。所有注册在日志文件下的事件源,都会被记录在日志文件的Sources值中(详见3.1中
日志文件值的Sources的描述)。注册表结构如下:
HKEY_LOCAL_MACHINE
SYSTEM
CurrentControlSet
Services
EventLog
Application
AppName
Security
System
DriverName
CustomLog
AppName
SYSTEM
CurrentControlSet
Services
EventLog
Application
AppName
Security
System
DriverName
CustomLog
AppName
应用程序可以通过给定RegisterEventSource函数事件源名字打开事件日志。日志文件中的事件源名字不能重复也不能分层(即,事件源名字不能用‘/’字符)。 每个事件源在注册表中都有自己的值,这些值为事件日志的还原提供了必要的信息,见下表:
名称
|
类型
|
描述
|
CategoryCount
|
REG_DWORD | 描述事件种类的数目。(可没有该值) |
CategoryMessageFile
|
REG_EXPAND_SZ | 描述种类消息(Message)文件的路径。种类消息文件中包含着语言相关的种类字符串。该字段可以包含多个文件,文件间用‘;’号分隔。(可没有该值) |
DisplayNameFile
|
REG_EXPAND_SZ |
描述存放事件日志本地化名字的文件。当该值不存在时,日志文件子键即为该值。
Windwos NT不支持该值。
|
DisplayNameID
|
REG_DWORD |
事件日志名在消息文件中的消息标识号。消息文件的名字存储在DisplayNameFile值中。
Windwos NT不支持该值。
|
EventMessageFile
|
REG_EXPAND_SZ | 描述事件消息(Message)文件的路径。事件消息文件中包含着语言相关的事件字符串。该字段可以包含多个文件,文件间用‘;’号分隔。该值必须存在,并且必须至少有一个文件。 |
ParameterMessageFile
|
REG_EXPAND_SZ |
描述参数消息(Message)文件的路径。参数消息文件中包含着语言相关的参数字符串。该字段可以包含多个文件,文件间用‘;’号分隔。
(可没有该值)
|
TypesSupported
|
REG_DWORD |
这是一个位掩码值,表示事件源支持的事件的类型,可以有以下值:
EVENTLOG_ERROR_TYPE 0x0001
EVENTLOG_WARNING_TYPE 0x0002 EVENTLOG_INFORMATION_TYPE 0x0004 EVENTLOG_AUDIT_SUCCESS 0x0008 EVENTLOG_AUDIT_FAILURE 0x0010 |
当没有添加新的事件源到注册表时,应用程序会用Application事件日志。如果应用程序在调用RegisterEventSource时,传入的事件源不在注册表中,默认情况下Event-logging服务会打开Application日志文件。但由于没有消息文件,日志的信息将无法正常的还原。因此,应该为事件日志应用程序在注册表中添加一个事件源并指定一个消息文件。
事件种类可以对事件日志进行较细的分类,Event View可以根据该字段进行事件日志过滤。每一个事件源都可以定义自己的有限的事件种类,可以为事件种类定义自己的ID以及字符串描述。事件种类的ID必须是从1开始的连续的数字。事件源支持的事件种类的数目在注册表的CategoryCount中存储。事件种类的ID是事件种类字符描述在种类消息文件中的索引,用该索引调用FormatMessage函数可得到事件种类的字符串描述。种类消息文件存储在注册表中事件源的CategoryMessageFile值中。当该值不存在时,EventMessageFile值表示该消息文件。
事件标识唯一的指定一个事件,但不同的事件源允许事件标识重复。每一个事件源可以定义自己的有限的事件,以及对应事件的描述消息。可以根据这些消息还原事件描述的问题。在定义事件描述消息时应尽量让消息简明清晰。
下面是事件标识的格式,它与WINDOWS的错误号的定义格式相同。在Event View当中我们看到的事件标识其实时下面格式中的Code部分,即低16位。
31 15 0
+---+-+-+-----------------------+-------------------------------+
|Sev|C|R| Facility | Code |
+----+-+-+---------------------------------+--------------------------------------------+
Sev :严重级别。可以有以下值
00 – 成功
01 – 信息
10 – 警告
11 – 错误
01 – 信息
10 – 警告
11 – 错误
C :0表示由WINDOWS操作系统编写占用,1表示其他应用程序占用
R :保留.
Facility :功能号。该值可以是FACILITY_NULL或以下的各值:
FACILITY_ACS
FACILITY_AAF
FACILITY_CERT
FACILITY_COMPLUS
FACILITY_CONTROL
FACILITY_DISPATCH
FACILITY_INTERNET
FACILITY_ITF
FACILITY_MEDIASERVER
FACILITY_MSMQ
FACILITY_RPC
FACILITY_SCARD
FACILITY_SECURITY
FACILITY_SETUPAPI
FACILITY_SSPI
FACILITY_STORAGE
FACILITY_URT
FACILITY_WIN32
FACILITY_WINDOWS
FACILITY_AAF
FACILITY_CERT
FACILITY_COMPLUS
FACILITY_CONTROL
FACILITY_DISPATCH
FACILITY_INTERNET
FACILITY_ITF
FACILITY_MEDIASERVER
FACILITY_MSMQ
FACILITY_RPC
FACILITY_SCARD
FACILITY_SECURITY
FACILITY_SETUPAPI
FACILITY_SSPI
FACILITY_STORAGE
FACILITY_URT
FACILITY_WIN32
FACILITY_WINDOWS
Code :功能代码的状态号。
事件标识(低16位)是事件描述字符串在事件消息文件中的索引,可以通过它调用FormatMessage函数获得事件的字符描述。事件消息文件由注册表中事件源的EventMessageFile值存储。事件描述是语言相关并要本地化的。事件描述中允许有占位符,格式为%n,n的范围是(1~99),%1是第一个。占位符表示要在该位置有插入字符串替换占位符。如下例,.MC文件中一项:
MessageId=0x4
Severity=Error
Facility=System
SymbolicName=MSG_CMD_DELETE
Language=English
File %1 contains %2, which is in error.
在这中情况下,ReadEventLog函数返回的缓冲区中包含“插入字符串”。EVENTLOGRECORD结构中的NumStrings字段就表示“插入字符串”的数目,而StringOffset字段定位“插入字符串”在缓冲区中的位置。
事件描述中还可以允许有为参数消息文件中的参数预留的占位符。占位符的格式为%%n,n的范围是(1~99),%%1中的1表示参数消息在参数消息文件中的标识,即参数消息的索引。该消息也可以用FormatMessage函数获得。参数消息文件存储在注册表中事件源的ParameterMessageFile值中。当该值不存在时,EventMessageFile值表示该消息文件。
还原事件描述,去掉事件描述中的占位符可以用函数FormatMessage。FormatMessage函数工作时,会将输入的字符串中的占位符去掉,然后在相同位置,按照占位符n的值插入与之对应的“插入字符串”。当在字符串中发现‘%’号,但不符合‘%n’格式时,函数将‘%’去掉,如:‘%%1’处理后为‘%1’。在还原事件描述时,当事件中即有“插入字符串”又有参数消息时,可以通过两次调用FormatMessage函数进行还原。
“插入字符串”对于事件描述是可选的且是语言无关的。因为它是非本地化的,所以这些信息最好是描述‘数值’、‘文件名’或‘用户名’的字符串,字符串的长度不要超过32K-1字节。“插入字符串”通常应在描述中被当作‘数据’来用,而不是‘语意’来用。因为事件描述是从语言相关的消息文件中获得的,如果“插入字符串”以‘语意’方式出现,那么会因为事件描述的语言与“插入字符串”语言的书写不同或语法不同而无法正确解释事件消息。
每个事件源都应该注册包含事件标识,事件种类,事件参数描述的消息文件。这些信息应该注册在着册表的EventMessageFile, CategoryMessageFile和 ParameterMessageFile值中。可以将事件标识,事件种类,事件参数的描述信息放在同一个文件当中,也可以分成三个文件。多个应用程序可以共享同一个消息文件。
应该把消息文件创建成为一个资源(resource-only)动态库,这样它将比普通动态库小且加载起来快。关于如何创建消息文件可以参看MSDN。
下面简单描述一下如何从消息文件中获得事件消息:
1. 调用RegOpenKeyEx函数打开事件源
2. 调用RegQueryValueEx函数获得事件源的EventMessageFile值。该值描述了事件消息的消息动态库的名字,有可能是多个文件。
3. 调用LoadLibraryEx函数加载由步骤2获得的动态库。
4. 调用FormatMessage函数,传入动态库的句柄及事件的ID号,获得对应的事件描述。如未获得事件描述,且消息文件不止一个,重复步骤3,4直到消息文件都处理完或得到了事件描述为止。获得事件描述后继续用FormatMessage函数将描述中的“插入字符串”与事件参数的占位符还原。
注:消息文件中的消息资源都是UNICODE编码的。
每一个事件都可以包含与事件相关的数据。EVENTLOGRECORD结构的DataLength字段表示这部分数据的长度,DataOffset字段定位数据在缓冲区中的偏移。事件可以在这里填写属于自己的必要的信息。在Event View当中我们可以看到,它显示这部分数据用十六进制模式或文本模式。Event View不知道如何解释这部分数据,只有事件知道它的真正含义。
可以用函数OpenEventLog,OpenBackupEventLog,RegisterEventSource,DeregisterEventSource,和CloseEventLog打开和关闭事件日志的句柄。通过事件日志句柄可以对事件日志进行下表描述的操作:
操作
|
函数
|
备份(Backup) | BackupEventLog |
清除(Clear) | ClearEventLog |
监控(Monitor) | NotifyChangeEventLog |
查询(Query) | GetOldestEventLogRecord,GetNumberOfEventLogRecords |
读(Read) | ReadEventLog |
写(Write) | ReportEvent |
OpenEventLog和ReportEvent有一个可选的服务器名参数,指明该操作可以远程操作。用OpenEventLog函数可以对获得日志读或进行管理(备份、清除、监视和查询)的权限。RegisterEventSource函数可以获得对日志进行写的权限。
对事件日志的所有操作都是原子操作。下面是Event-logging服务处理的几个特殊情况:
u Event-logging服务同时获得读写操作。如果读操作到了文件尾部,且写操作还没有完成,返回"end-of-file"状态,读操作失败;如果写操作已经完成,返回新记录。
u Event-logging服务在获得读操作前完成清除操作。读操作失败,返回"end-of-file"状态。
u Event-logging服务在获得写操作前完成清除操作。清除操作清空文件,写操作添加新事件日志作为日志文件的开始。
EVENT LOG的访问权限根据运行应用程序的用户帐号的不同而有区别。LocalSystem用户是系统特殊帐号,一般服务运用该帐号。Administraotr是由系统管理员帐号构成(Administraotrs组)。ServerOp是由域管理员帐号构成。World表示所有系统上的所有用户帐号。
Log
|
Account
|
Access
|
|
|
Application | LocalSystem | Read | Write | Clear |
Administrator | Read | Write | Clear | |
ServerOp | Read | Write | Clear | |
World | Read | Write | ||
System | LocalSystem | Read | Write | Clear |
Administrator | Read | Write | Clear | |
ServerOp | Read | Clear | ||
World | Read |
Security日志是专为系统设计的,只有Local Security Authority拥有写安全日志的权限。读或清除权限可以通过以下两种授权方式设定:
1. 控制面板—》管理工具—》本地安全设置—》本地策略—》用户权利指派—》管理审核和安全日志(manage auditing and security log)。为该项添加要拥有此项权限的用户帐号或用户组。管理员组默认拥有该权限。(以上为Win2000下的操作方式)
2.为用户帐号设定SE_SECURITY_NAME权限(编程实现)。
参考:《Windows Event Logging》,《MSDN》
作者:colorknight