Skip to content

Commit

Permalink
Display record count while loading all events in local Event Log
Browse files Browse the repository at this point in the history
  • Loading branch information
kmaki565 committed Feb 25, 2024
1 parent af9a21b commit e227aeb
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 27 deletions.
51 changes: 41 additions & 10 deletions EventLook/Model/DataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Text;
using System.Threading;
Expand All @@ -11,42 +12,49 @@ namespace EventLook.Model;

public interface IDataService
{
Task<int> ReadEvents(LogSource eventSource, DateTime fromTime, DateTime toTime, bool readFromNew, IProgress<ProgressInfo> progress);
Task<int> ReadEvents(LogSource logSource, DateTime fromTime, DateTime toTime, bool readFromNew, IProgress<ProgressInfo> progress);
void Cancel();
/// <summary>
/// Subscribes to events in an event log channel. The caller will be reported whenever a new event comes in.
/// </summary>
/// <param name="eventSource"></param>
/// <param name="logSource"></param>
/// <param name="handler"></param>
/// <returns>True if success.</returns>
bool SubscribeEvents(LogSource eventSource, IProgress<ProgressInfo> progress);
bool SubscribeEvents(LogSource logSource, IProgress<ProgressInfo> progress);
void UnsubscribeEvents();
}
public class DataService : IDataService
{
private CancellationTokenSource cts;
public async Task<int> ReadEvents(LogSource eventSource, DateTime fromTime, DateTime toTime, bool readFromNew, IProgress<ProgressInfo> progress)
public async Task<int> ReadEvents(LogSource logSource, DateTime fromTime, DateTime toTime, bool readFromNew, IProgress<ProgressInfo> progress)
{
using (cts = new CancellationTokenSource())
{
// Event records to be sent to the ViewModel
var eventRecords = new List<EventRecord>();
EventLogReader reader = null;
string errMsg = "";
int count = 0;
int totalCount = 0;
bool isFirst = true;
try
{
string sQuery = string.Format(" *[System[TimeCreated[@SystemTime > '{0}' and @SystemTime <= '{1}']]]",
fromTime.ToUniversalTime().ToString("o"),
toTime.ToUniversalTime().ToString("o"));

var elQuery = new EventLogQuery(eventSource.Path, eventSource.PathType, sQuery)
var elQuery = new EventLogQuery(logSource.Path, logSource.PathType, sQuery)
{
ReverseDirection = readFromNew
};
var reader = new EventLogReader(elQuery);
var eventRecord = reader.ReadEvent();
Debug.WriteLine("Begin Reading");

// Asynchronously get the record count info.
// The count is valid only when "All time" is selected for a Event Log on the local machine.
_ = Task.Run(() => totalCount = GetRecordCount(logSource));

reader = new EventLogReader(elQuery);
var eventRecord = reader.ReadEvent();
await Task.Run(() =>
{
for (; eventRecord != null; eventRecord = reader.ReadEvent())
Expand All @@ -57,7 +65,7 @@ await Task.Run(() =>
++count;
if (count % 100 == 0)
{
var info = new ProgressInfo(eventRecords.ConvertAll(e => new EventItem(e)), isComplete: false, isFirst);
var info = new ProgressInfo(eventRecords.ConvertAll(e => new EventItem(e)), isComplete: false, isFirst, totalCount);
cts.Token.ThrowIfCancellationRequested();
progress.Report(info);
isFirst = false;
Expand All @@ -84,8 +92,9 @@ await Task.Run(() =>
}
finally
{
var info_comp = new ProgressInfo(eventRecords.ConvertAll(e => new EventItem(e)), isComplete: true, isFirst, errMsg);
var info_comp = new ProgressInfo(eventRecords.ConvertAll(e => new EventItem(e)), isComplete: true, isFirst, totalCount, errMsg);
progress.Report(info_comp);
reader?.Dispose();
Debug.WriteLine("End Reading");
}
return count;
Expand Down Expand Up @@ -139,7 +148,7 @@ private void EventRecordWrittenHandler(object sender, EventRecordWrittenEventArg
}
catch (Exception ex)
{
progress?.Report(new ProgressInfo(new List<EventItem>(), isComplete: true, isFirst: true, ex.Message));
progress?.Report(new ProgressInfo(new List<EventItem>(), isComplete: true, isFirst: true, totalEventCount: 0, ex.Message));
}
}

Expand Down Expand Up @@ -198,4 +207,26 @@ public static bool IsValidEventLog(string path, PathType type)
}
}
}

/// <summary>
/// Gets the total number of event records in a channel.
/// This refers to the RecordCount property in EventLogInformation instead of iterating ReadEvent() to count.
/// So the count does not necessarily match the number of records to be received by the query.
/// </summary>
private static int GetRecordCount(LogSource source)
{
if (source.PathType == PathType.LogName)
{
try
{
using (var elSession = new EventLogSession())
{
EventLogInformation info = elSession.GetLogInformation(source.Path, PathType.LogName);
return Convert.ToInt32(info.RecordCount ?? 0); // Int should be large enough.
}
}
catch (Exception) { }
}
return 0;
}
}
24 changes: 11 additions & 13 deletions EventLook/Model/Progress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@

namespace EventLook.Model;

public class ProgressInfo
public class ProgressInfo(IReadOnlyList<EventItem> eventItems, bool isComplete, bool isFirst, int totalEventCount = 0, string message = "")
{
public ProgressInfo(IReadOnlyList<EventItem> eventItems, bool isComplete, bool isFirst, string message = "")
{
LoadedEvents = eventItems;
IsComplete = isComplete;
IsFirst = isFirst;
Message = message;
}
public IReadOnlyList<EventItem> LoadedEvents { get; } = eventItems;

public IReadOnlyList<EventItem> LoadedEvents { get; }

public bool IsComplete { get; }
public bool IsFirst { get; }
public string Message { get; set; }
public bool IsComplete { get; } = isComplete;
public bool IsFirst { get; } = isFirst;
/// <summary>
/// Record count information for the local Event Log.
/// Can be 0 if the count is not determined yet or not applicable.
/// For example, when auto refresh is on, or for a .evtx file, it's always 0.
/// </summary>
public int RecordCountInfo { get; } = totalEventCount;
public string Message { get; } = message;
}
10 changes: 6 additions & 4 deletions EventLook/View/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,20 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu

/// <summary>
/// Generates the status text based on the various indicators.
/// 0: IsUpdating, 1: IsAutoRefreshing, 2: IsAppend, 3: LoadedEventCount, 4: AppendCount, 5: LastElapsedTime, 6: ErrorMessage
/// 0: IsUpdating, 1: IsAutoRefreshing, 2: IsAppend, 3: LoadedEventCount, 4: TotalEventCount, 5: AppendCount, 6: LastElapsedTime, 7: ErrorMessage
/// </summary>
public class StatusTextConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is bool isUpdating && values[1] is bool isAutoRefreshing && values[2] is bool isAppend
&& values[3] is int loadedEventCount && values[4] is int appendCount
&& values[5] is TimeSpan lastEpasedTime && values[6] is string errorMessage)
&& values[3] is int loadedEventCount && values[4] is int totalEventCount && values[5] is int appendCount
&& values[6] is TimeSpan lastEpasedTime && values[7] is string errorMessage)
{
if (isUpdating)
return $"Loading {loadedEventCount} events... {errorMessage}";
return totalEventCount > 0
? $"Loading {loadedEventCount}/{totalEventCount} events... {errorMessage}"
: $"Loading {loadedEventCount} events... {errorMessage}";
else if (isAutoRefreshing)
return $"{loadedEventCount} events loaded. Waiting for new events... {errorMessage}";
else
Expand Down
1 change: 1 addition & 0 deletions EventLook/View/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
<Binding Path="IsAutoRefreshing"/>
<Binding Path="IsAppend"/>
<Binding Path="LoadedEventCount"/>
<Binding Path="TotalEventCount"/>
<Binding Path="AppendCount"/>
<Binding Path="LastElapsedTime"/>
<Binding Path="ErrorMessage"/>
Expand Down
7 changes: 7 additions & 0 deletions EventLook/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ public bool IsAutoRefreshing
private int loadedEventCount = 0;
public int LoadedEventCount { get => loadedEventCount; set => SetProperty(ref loadedEventCount, value); }

private int totalEventCount = 0;
public int TotalEventCount { get => totalEventCount; set => SetProperty(ref totalEventCount, value); }

private bool isAppend = false;
public bool IsAppend { get => isAppend; set => SetProperty(ref isAppend, value); }

Expand Down Expand Up @@ -408,6 +411,7 @@ private async Task Update(Task task)
{
ongoingTask = task;
stopwatch.Restart();
TotalEventCount = 0;
if (!IsAppend)
LoadedEventCount = 0;
IsUpdating = true;
Expand Down Expand Up @@ -442,6 +446,8 @@ private void ProgressCallback(ProgressInfo progressInfo)
}

LoadedEventCount = Events.Count;
// Disregard unless the range is "All time".
TotalEventCount = (SelectedRange.DaysFromNow == 0 && !SelectedRange.IsCustom) ? progressInfo.RecordCountInfo : 0;
ErrorMessage = progressInfo.Message;

if (progressInfo.IsComplete)
Expand Down Expand Up @@ -473,6 +479,7 @@ private void TurnOnAutoRefresh()
if (!IsAppend)
Refresh(reset: false, append: true);
DataService.SubscribeEvents(SelectedLogSource, progressAutoRefresh);
TotalEventCount = 0;
IsAutoRefreshing = true;
}
private void TurnOffAutoRefresh()
Expand Down

0 comments on commit e227aeb

Please sign in to comment.