Initial Commit
This commit is contained in:
78
PatchProbe.Cli/Collectors/EventCollector.cs
Normal file
78
PatchProbe.Cli/Collectors/EventCollector.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PatchProbe.Shared.Contracts;
|
||||
using PatchProbe.Shared.Models;
|
||||
|
||||
namespace PatchProbe.Cli.Collectors;
|
||||
|
||||
public sealed class EventCollector(ILogger<EventCollector> logger) : ICollector<List<UpdateEvent>>
|
||||
{
|
||||
private static readonly (string LogName, string? Provider)[] Sources =
|
||||
[
|
||||
("System", null),
|
||||
("Microsoft-Windows-WindowsUpdateClient/Operational", "Microsoft-Windows-WindowsUpdateClient"),
|
||||
("Microsoft-Windows-UpdateOrchestrator/Operational", "Microsoft-Windows-UpdateOrchestrator"),
|
||||
];
|
||||
|
||||
private const int LookbackHours = 72;
|
||||
private const int MaxEventsPerSource = 50;
|
||||
|
||||
public Task<List<UpdateEvent>> CollectAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
logger.LogInformation("Collecting recent Windows Update events (last {Hours}h)", LookbackHours);
|
||||
|
||||
var events = new List<UpdateEvent>();
|
||||
var since = DateTime.UtcNow.AddHours(-LookbackHours);
|
||||
|
||||
foreach (var (logName, provider) in Sources)
|
||||
{
|
||||
try
|
||||
{
|
||||
events.AddRange(ReadEvents(logName, provider, since));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogDebug(ex, "Could not read event log: {LogName}", logName);
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation("Collected {Count} update events", events.Count);
|
||||
return Task.FromResult(events);
|
||||
}
|
||||
|
||||
private static IEnumerable<UpdateEvent> ReadEvents(string logName, string? provider, DateTime since)
|
||||
{
|
||||
var providerFilter = provider != null ? $" and Provider[@Name='{provider}']" : string.Empty;
|
||||
var query = new EventLogQuery(
|
||||
logName,
|
||||
PathType.LogName,
|
||||
$"*[System[(Level<=3){providerFilter} and TimeCreated[@SystemTime>='{since:yyyy-MM-ddTHH:mm:ss.000Z}']]]");
|
||||
|
||||
using var reader = new EventLogReader(query);
|
||||
int count = 0;
|
||||
while (reader.ReadEvent() is EventRecord record && count < MaxEventsPerSource)
|
||||
{
|
||||
using (record)
|
||||
{
|
||||
yield return new UpdateEvent
|
||||
{
|
||||
EventId = record.Id,
|
||||
Source = record.ProviderName,
|
||||
LogName = record.LogName,
|
||||
Level = record.LevelDisplayName,
|
||||
TimeCreated = record.TimeCreated.HasValue
|
||||
? new DateTimeOffset(record.TimeCreated.Value, TimeSpan.Zero)
|
||||
: null,
|
||||
Message = TryFormatMessage(record),
|
||||
};
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string? TryFormatMessage(EventRecord record)
|
||||
{
|
||||
try { return record.FormatDescription(); }
|
||||
catch { return null; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user