[英]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.Entries
和EventLogEntry
,那么至少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.