简体   繁体   English

从System.Diagnostics.EventLog优化LINQ读取

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

I have a performance problem on certain computers with the following query: 我在使用以下查询的某些计算机上遇到性能问题:

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();

Apparently ToList() can be quite slow in certain queries, but with what should I replace it? 显然ToList()在某些查询中可能会很慢,但我应该用它替换它?

log.Entries collection works like this: it is aware about total number of events ( log.Entries.Count ) and when you access individual element - it makes a query to get that element. log.Entries集合的工作原理如下:它知道事件总数( log.Entries.Count )以及访问单个元素时它会查询获取该元素。

That means when you enumerate over whole Entries collection - it will query for each individual element, so there will be Count queries. 这意味着当您枚举整个Entries集合时 - 它将查询每个单独的元素,因此将有Count查询。 And structure of your LINQ query (for example, OrderBy ) forces full enumeration of that collection. LINQ查询的结构(例如, OrderBy )强制完全枚举该集合。 As you already know - it's very inefficient. 正如您所知 - 这是非常低效的。

Much more efficient might be to query only log entries you need. 更高效的可能是仅查询您需要的日志条目。 For that you can use EventLogQuery class. 为此,您可以使用EventLogQuery类。 Suppose you have simple class to hold event info details: 假设您有简单的类来保存事件信息详细信息:

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

Then you can convert your inefficient LINQ query like this: 然后你可以转换你的低效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;
        }
    }
}

It will be 10-100 times faster. 它将快10到100倍。 However, this API is more low-level and returns instances of EventRecord (and not EventLogEntry ), so for some information there might be different ways to obtain it (compared to EventLogEntry ). 但是,此API更低级并返回EventRecord实例(而不是EventLogEntry ),因此对于某些信息,可能有不同的方法来获取它(与EventLogEntry相比)。

If you decide that you absolutely must use log.Entries and EventLogEntry , then at least enumerate Entries backwards. 如果您决定绝对必须使用log.EntriesEventLogEntry ,那么至少log.Entries后枚举Entries That's because newest events are in the end (its sorted by timestamp ascending), and you need top X errors by timestamp descended. 这是因为最新事件最终(按时间戳升序排序),并且您需要按时间戳下降的前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;
    }
}

That is less efficient, but still should be 10 times faster than your current approach. 效率较低,但仍应比现有方法快10倍。 Note that it's faster because Entries collection is not materialized in memory. 请注意,它更快,因为Entries集合未在内存中实现。 Individual elements are queried when you access them, and when enumerating backwards in your specific case - there is high chance to query much less elements. 当您访问它们时会查询单个元素,并且在您的特定情况下向后枚举时 - 很有可能查询更少的元素。

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

相关问题 System.Diagnostics.EventLog是否正在泄漏内存? - Is System.Diagnostics.EventLog leaking memory? 是否有等效于C ++的System.Diagnostics.EventLog? - Is there an equivalent of System.Diagnostics.EventLog for C++? 无法为 System.Diagnostics.EventLog 创建 Mole 类型 - Can't Create Mole Type for System.Diagnostics.EventLog 如何使用System.Diagnostics.EventLog从任意evxt文件读取? - How do I read from an arbitrary evxt file using System.Diagnostics.EventLog? 无法使用实例引用访问System.Diagnostics.EventLog - System.Diagnostics.EventLog Cannot be Accessed with an Instance Reference C# System.Diagnostics.EventLog.GetEventLogs - C# System.Diagnostics.EventLog.GetEventLogs 读取System.Diagnostics.ProcessStartInfo标准输出为字节而不是字符 - Reading System.Diagnostics.ProcessStartInfo StandardOutput as bytes instead of chars 从事件日志中读取。 错误:在名称空间中找不到类型名称“ EventLog” - Reading from eventlog. Error: The type name 'EventLog' could not be found in the namespace 从 System.Diagnostics.Debug 中删除引用 - Delete references from System.Diagnostics.Debug 从完整的EventLog中读取时,神秘的IndexOutOfRange异常再次发生 - A mystery IndexOutOfRange Exception recurring when reading from a full EventLog
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM