79 lines
2.8 KiB
C#
79 lines
2.8 KiB
C#
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; }
|
|
}
|
|
}
|