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.Entries
и EventLogEntry
, то хотя бы перечислите 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
коллекция не материализована в памяти.Отдельные элементы запрашиваются при доступе к ним и при перечислении в обратном порядке в вашем конкретном случае - существует высокая вероятность запроса гораздо меньшего количества элементов.