Оптимизация чтения LINQ из System.Diagnostics.EventLog - PullRequest
0 голосов
/ 18 мая 2018

У меня проблема с производительностью на некоторых компьютерах со следующим запросом:

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() может быть довольно медленным в некоторых запросах, но чем мне его заменить?

1 Ответ

0 голосов
/ 18 мая 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...