1

检查一段时间内事件日志中是否记录了事件的最简单方法是什么?

我想执行一系列自动化测试步骤,然后检查是否有任何错误记录到应用程序事件日志中,忽略一些我不感兴趣的来源。我可以使用 System.Diagnostics.EventLog 然后查看条目集合,但在这种情况下似乎不是很有用。例如,如果事件日志正在删除旧条目,Entries.Count 会随着时间的推移而变小。我更喜欢某种方式来查询日志或监视它在一段时间内的变化。例如

DateTime start = DateTime.Now;
// do some stuff...
foreach(EventLogEntry entry in CleverSolution.EventLogEntriesSince(start, "Application"))
{ 
  // Now I can do stuff with entry, or ignore if its Source is one
  // that I don't care about.
  // ...
}
4

5 回答 5

5

只是为了成为一名优秀的 Wiki 公民并努力完成,还有其他方法。我之前没有建议它,因为它对于仅作为测试套件的一部分在内部运行的东西来说完全是矫枉过正,而且你在标题中说你想要一些简单的东西。

但是,如果您需要查看运输代码中发生的事件,请继续阅读。信不信由你,目前这件事有三种不同的 Windows API。

通知更改事件日志()

这类事情的原始 API 称为NotifyChangeEventLog(),从 Windows 2000 开始就支持它。本质上,您使用WIN32 事件日志 API打开事件日志,然后使用另一个给定的句柄调用此 API API 和事件句柄。当有新的事件日志条目可供查看时,Windows 将发出您的事件信号。

我自己从未使用过这个 API,因为我最感兴趣的是远程事件日志访问,而这个 API 明确不支持远程日志。但是,如果您拥有正确的权限,它所属的 API 集的其余部分确实可以让您按顺序读取远程日志。

Windows 管理规范

第二种方法是使用Windows Management Instrumentation API,它支持本地和远程日志。这是一个基于 COM/DCOM 的 API,在 Windows 中已经存在了好几年,.NET Framework 在System.Management命名空间中有一个很好的实现。本质上,您所做的是创建一个EventQuery来查找Win32_NTLogEvent类型(在 WMI 类型系统内)的新 WMI 对象的外观。这些的出现将指示新的事件日志条目,并且它们将几乎实时呈现。这些对象的属性包含日志条目的所有详细信息。有一篇来自 MSDN 杂志的文章谈论在 Visual Studio 中玩这些东西。

同样,这对于测试应用程序来说完全是多余的,它需要比现有解决方案更多的代码。但是几年前,我为一个网络管理应用程序编写了一个子系统,它使用这个 API 的 DCOM 风格来收集网络上所有服务器的事件日志,以便我们可以对特定的服务器发出警报。它几乎是实时的。如果您使用 DCOM 在 C++ 中实现此功能,请准备好处理多线程单元和大量复杂的逻辑来检测您与远程服务器的连接是否/何时启动或关闭。

Windows Vista 事件日志

Windows Vista(和 Server 2008)有一个全新的与事件记录和跟踪相关的 API 套件。此处记录了新的事件日志。看起来有一个名为 EvtSubscribe 的 API 允许您订阅事件。我没有使用过这个 API,所以我不能评论它的优缺点。

于 2008-10-08T16:08:49.353 回答
3

话虽如此,这里的答案实际上应该非常简单,即使对于您的测试应用程序并且是特定于 .NET Framework 的。

您需要在开始测试之前打开 EventLog,并将事件处理程序订阅到EventLog.EntryWritten事件。这是 .NET 公开 NotifyChangeEventLog() Win32 API 的方式。

将您当前的逻辑从GetEventLogEntriesSince()事件处理程序中移出,但不要将事件添加到列表中以进行返回,而是将它们存储在一个列表中,您可以在运行结束时从某个地方检索它们。您可以通过其Entry属性从传递的 EntryWrittenEventArgs 参数中检索日志条目的内容。

于 2008-10-08T16:53:38.293 回答
1

System.Diagnostics.EventLog类确实是执行此操作的正确方法。

您的主要反对意见似乎是日志在某些情况下可以删除旧条目。但是您说这是在软件测试场景中。您不能安排配置您的测试系统,使日志足够大以包含所有条目,并且在您的测试期间不会删除旧条目?

于 2008-10-08T12:56:52.203 回答
0

那么我提出的解决方案确实使用System.Diagnostics.EventLog并简单地迭代所有事件以过滤我想要的事件。我想这很简单,我只是认为会有一个更有效的界面。非常欢迎任何建议或改进!

我创建了一种方法来返回某个时间以来的事件日志条目:

/// <summary>
/// Steps through each of the entries in the specified event log and returns any that were written 
/// after the given point in time. 
/// </summary>
/// <param name="logName">The event log to inspect, eg "Application"</param>
/// <param name="writtenSince">The point in time to return entries from</param>
/// <param name="type">The type of entry to return, or null for all entry types</param>
/// <returns>A list of all entries of interest, which may be empty if there were none in the event log.</returns>
public List<EventLogEntry> GetEventLogEntriesSince(string logName, DateTime writtenSince, EventLogEntryType type)
{
    List<EventLogEntry> results = new List<EventLogEntry>();
    EventLog eventLog = new System.Diagnostics.EventLog(logName);
    foreach (EventLogEntry entry in eventLog.Entries)
    {
        if (entry.TimeWritten > writtenSince && (type==null || entry.EntryType == type))
            results.Add(entry);
    }
    return results;
}

在我的测试类中,我存储了一个时间戳:

private DateTime whenLastEventLogEntryWritten;

在测试设置期间,我将时间戳设置为写入最后一个事件日志条目的时间:

EventLog eventLog = new EventLog("Application");
whenLastEventLogEntryWritten = eventLog.Entries.Count > 0 ? 
     eventLog.Entries[eventLog.Entries.Count - 1] : DateTime.Now;

在我的测试结束时,我检查没有事件日志错误:

Assert.IsEmpty(GetEventLogEntriesSince("Application",
                                       whenLastEventLogEntryWritten,  
                                       EventLogEntryType.Error), 
               "Application Event Log errors occurred during test execution.");
于 2008-10-08T13:21:34.447 回答
0

我使用此 Powershell 在过去 7 天内扫描事件日志以查找相关条目:

$d=Get-Date
$recent=[System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($d.AddDays(-7))

get-wmiobject -computer HOSTNAME -class Win32_NTLogEvent `
    -filter "logfile = 'Application' and (sourcename = 'SOURCENAME' or sourcename like 'OTHERSOURCENAME%') and (type = 'error' or type = 'warning') AND (TimeGenerated >='$recent')" | 
sort-object @{ expression = {$_.TimeWritten} } -descending |
select SourceName, Message | 
format-table @{Expression = { $_.SourceName};Width = 20;Label="SourceName"}, Message

如果您使用 C#(已标记,但问题中未提及),那么神奇之处在于 get-wmiobject 查询。

于 2008-10-08T17:02:30.617 回答