Source/Tx.Windows.Logs/EvtxEnumerable.cs (97 lines of code) (raw):

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Eventing.Reader; using System.Linq; using System.Reactive; using System.Xml.Linq; namespace Tx.Windows { public static class EvtxEnumerable { public static IEnumerable<EventLogRecord> FromFiles(params string[] logfiles) { if (logfiles.Length == 1) return FromFile(logfiles[0]); IEnumerable<IEnumerator<EventLogRecord>> inputs = from file in logfiles select FromFile(file).GetEnumerator(); return new PullMergeSort<EventLogRecord>(e => e.TimeCreated.Value.ToUniversalTime(), inputs); } /// <summary> /// Creates multiple log readers as specified in the XML /// which has the same format as Windows Event Collection /// Note that logs that don't exist on the local machine are skipped /// </summary> /// <param name="wecXml">XML configuration about XPath expressions over multiple logs</param> /// <returns></returns> public static IEnumerable<EventLogRecord> FromWecXml(string wecXml) { if (string.IsNullOrEmpty(wecXml)) { throw new ArgumentNullException(nameof(wecXml)); } var readers = new List<IEnumerable<EventLogRecord>>(); XDocument xd = XDocument.Parse(wecXml); foreach (var query in xd.Root.Elements("Query")) { string logName = query.Attribute("Path").Value; foreach (var select in query.Elements("Select")) { if (EventLog.Exists(logName)) { readers.Add(FromLogQuery(logName, select.Value, null)); } } } return new PullMergeSort<EventLogRecord>( r => r.TimeCreated.Value, readers.Select(r => r.GetEnumerator())); } public static IEnumerable<EventLogRecord> FromLogQuery(string logName, string xpathQuery, EventBookmark bookmark) { long eventCount = 0; // for debugging EventLogQuery query = new EventLogQuery(logName, PathType.LogName, xpathQuery); using (var reader = new EventLogReader(query, bookmark)) { for (; ; ) { if (!(reader.ReadEvent() is EventLogRecord record)) { yield break; } eventCount++; yield return record; } } } private static IEnumerable<EventLogRecord> FromFile(string logFile) { IEnumerable<EventLogRecord> result = null; try { result = ReadFile(logFile); } catch (EventLogException eventLogEx) { if (eventLogEx.Message.IndexOf(EventLogReaderExceptions.ArrayBoundsInvalid, StringComparison.OrdinalIgnoreCase) > 0) { result = ReadFile(logFile, true); } } return result; } private static IEnumerable<EventLogRecord> ReadFile(string logFile, bool reduceReaderBatchSize = false) { long eventCount = 0; // for debugging using (var reader = new EventLogReader(logFile, PathType.FilePath)) { // There is an acceptable limit to the event size. // Per batch, by default, the reader reads 64 events. // If event size > acceptable limit, // the reader throws "Array bounds are invalid". // To fix this, the batch size needs to be reduced. // This could have some performance impact, but the // log is readable and not written off as corrupt. if (reduceReaderBatchSize) { reader.BatchSize = 1; } for (; ; ) { if (!(reader.ReadEvent() is EventLogRecord record)) { yield break; } eventCount++; yield return record; } } } } }