使用.net检查事件日志中是否记录了事件的最简单方法是什么

本文关键字:事件 最简单 记录 方法 是什么 是否 net 检查 日志 使用 | 更新日期: 2023-09-27 17:47:48

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

我想执行一系列自动测试步骤,然后检查是否有任何错误记录到应用程序事件日志中,忽略一些我不感兴趣的源。我可以使用System.Diagnostics.EventLog,然后查看Entries集合,但它似乎不太适用于此场景。例如,如果事件日志正在删除旧条目,则随着时间的推移,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.
  // ...
}

使用.net检查事件日志中是否记录了事件的最简单方法是什么

要成为一个好的Wiki公民并努力完成,还有其他方法。我之前没有建议这样做,因为对于只作为测试套件的一部分在内部运行的东西来说,这完全是小题大做,而你在标题中说你想要一些简单的东西。

但是,如果你需要在发货代码中看到事件的发生,请继续阅读。信不信由你,目前有三个不同的Windows API。

NotifyChangeEventLog()

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

我自己从来没有使用过这个API,因为我最感兴趣的是远程事件日志访问,而这个API明确地不支持远程日志。然而,这属于的API集的其余部分允许您在拥有正确权限的情况下顺序读取远程日志。

Windows Management Instrumentation

第二种方法是使用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事件日志

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

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

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

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

System.Diagnostics.EventLog类确实是实现这一点的正确方法。

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

我提出的解决方案确实使用了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.");

我使用这个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查询。