繁体   English   中英

从System.Diagnostics.EventLog优化LINQ读取

[英]Optimizing a LINQ reading from System.Diagnostics.EventLog

我在使用以下查询的某些计算机上遇到性能问题:

System.Diagnostics.EventLog log = new System.Diagnostics.EventLog("Application");

var entries = log.Entries
    .Cast<System.Diagnostics.EventLogEntry>()
    .Where(x => x.EntryType == System.Diagnostics.EventLogEntryType.Error)
    .OrderByDescending(x => x.TimeGenerated)
    .Take(cutoff)
    .Select(x => new
    {
        x.Index,
        x.TimeGenerated,
        x.EntryType,
        x.Source,
        x.InstanceId,
        x.Message
    }).ToList();

显然ToList()在某些查询中可能会很慢,但我应该用它替换它?

log.Entries集合的工作原理如下:它知道事件总数( log.Entries.Count )以及访问单个元素时它会查询获取该元素。

这意味着当您枚举整个Entries集合时 - 它将查询每个单独的元素,因此将有Count查询。 LINQ查询的结构(例如, OrderBy )强制完全枚举该集合。 正如您所知 - 这是非常低效的。

更高效的可能是仅查询您需要的日志条目。 为此,您可以使用EventLogQuery类。 假设您有简单的类来保存事件信息详细信息:

private class EventLogInfo {
    public int Id { get; set; }
    public string Source { get; set; }
    public string Message { get; set; }
    public DateTime? Timestamp { get; set; }
}

然后你可以转换你的低效LINQ查询,如下所示:

// query Application log, only entries with Level = 2 (that's error)
var query = new EventLogQuery("Application", PathType.LogName, "*[System/Level=2]");
// reverse default sort, by default it sorts oldest first
// but we need newest first (OrderByDescending(x => x.TimeGenerated)
query.ReverseDirection = true;            
var events = new List<EventLogInfo>();
// analog of Take
int cutoff = 100;
using (var reader = new EventLogReader(query)) {
    while (true) {
        using (var next = reader.ReadEvent()) {
            if (next == null)
                // we are done, no more events
                break;
            events.Add(new EventLogInfo {
                Id = next.Id,
                Source = next.ProviderName,
                Timestamp = next.TimeCreated,
                Message = next.FormatDescription()
            });
            cutoff--;
            if (cutoff == 0)
                // we are done, took as much as we need
                break;
        }
    }
}

它将快10到100倍。 但是,此API更低级并返回EventRecord实例(而不是EventLogEntry ),因此对于某些信息,可能有不同的方法来获取它(与EventLogEntry相比)。

如果您决定绝对必须使用log.EntriesEventLogEntry ,那么至少log.Entries后枚举Entries 这是因为最新事件最终(按时间戳升序排序),并且您需要按时间戳下降的前X个错误。

EventLog log = new System.Diagnostics.EventLog("Application");
int cutoff = 100;
var events = new List<EventLogEntry>();
for (int i = log.Entries.Count - 1; i >= 0; i--) {
    // note that line below might throw ArgumentException
    // if, for example, entries were deleted in the middle
    // of our loop. That's rare condition, but robust code should handle it
    var next = log.Entries[i];
    if (next.EntryType == EventLogEntryType.Error) {
        // add what you need here
        events.Add(next);
        // got as much as we need, break
        if (events.Count == cutoff)
            break;
    }
}

效率较低,但仍应比现有方法快10倍。 请注意,它更快,因为Entries集合未在内存中实现。 当您访问它们时会查询单个元素,并且在您的特定情况下向后枚举时 - 很有可能查询更少的元素。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM