From 1231319c3702b79e5a99cddff0d2b750235d25df Mon Sep 17 00:00:00 2001 From: Bill Long Date: Sat, 7 Jan 2023 11:40:33 -0600 Subject: [PATCH] Fix Task name resolution - Tasks from the modern providers are ints, not longs. So are Opcodes. Update the model to reflect this. - Tasks must be resolvable from the modern provider even when the description comes from the legacy provider. --- .../EventProviderDbContext.cs | 4 +- .../EventResolvers/EventResolverBase.cs | 37 +++++--- .../Providers/EventMessageProvider.cs | 18 ++-- .../Providers/ProviderDetails.cs | 4 +- .../EventLogExpert.Test.csproj | 9 +- src/EventLogExpert.Test/UnitTest1.cs | 94 +++++++++++++++++++ 6 files changed, 131 insertions(+), 35 deletions(-) diff --git a/src/EventLogExpert.Library/EventProviderDatabase/EventProviderDbContext.cs b/src/EventLogExpert.Library/EventProviderDatabase/EventProviderDbContext.cs index 388df1ab..a54d1551 100644 --- a/src/EventLogExpert.Library/EventProviderDatabase/EventProviderDbContext.cs +++ b/src/EventLogExpert.Library/EventProviderDatabase/EventProviderDbContext.cs @@ -48,10 +48,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .Property(e => e.Opcodes) - .HasConversion>>(); + .HasConversion>>(); modelBuilder.Entity() .Property(e => e.Tasks) - .HasConversion>>(); + .HasConversion>>(); } } diff --git a/src/EventLogExpert.Library/EventResolvers/EventResolverBase.cs b/src/EventLogExpert.Library/EventResolvers/EventResolverBase.cs index e566570f..53f94960 100644 --- a/src/EventLogExpert.Library/EventResolvers/EventResolverBase.cs +++ b/src/EventLogExpert.Library/EventResolvers/EventResolverBase.cs @@ -131,26 +131,33 @@ protected DisplayEventModel ResolveFromProviderDetails(EventRecord eventRecord, if (description == null) { - var potentialTaskNames = providerDetails.Messages?.Where(m => m.ShortId == eventRecord.Task && m.LogLink != null && m.LogLink == eventRecord.LogName).ToList(); - if (potentialTaskNames != null && potentialTaskNames.Any()) + description = providerDetails.Messages?.FirstOrDefault(m => m.ShortId == eventRecord.Id)?.Text; + description = FormatDescription(eventRecord, description); + } + + if (taskName == null && eventRecord.Task.HasValue) + { + providerDetails.Tasks.TryGetValue(eventRecord.Task.Value, out taskName); + if (taskName == null) { - taskName = potentialTaskNames[0].Text; + var potentialTaskNames = providerDetails.Messages?.Where(m => m.ShortId == eventRecord.Task && m.LogLink != null && m.LogLink == eventRecord.LogName).ToList(); + if (potentialTaskNames != null && potentialTaskNames.Any()) + { + taskName = potentialTaskNames[0].Text; - if (potentialTaskNames.Count > 1) + if (potentialTaskNames.Count > 1) + { + _tracer("More than one matching task ID was found."); + _tracer($" eventRecord.Task: {eventRecord.Task}"); + _tracer(" Potential matches:"); + potentialTaskNames.ForEach(t => _tracer($" {t.LogLink} {t.Text}")); + } + } + else { - _tracer("More than one matching task ID was found."); - _tracer($" eventRecord.Task: {eventRecord.Task}"); - _tracer(" Potential matches:"); - potentialTaskNames.ForEach(t => _tracer($" {t.LogLink} {t.Text}")); + taskName = eventRecord.Task == null | eventRecord.Task == 0 ? "None" : $"({eventRecord.Task})"; } } - else - { - taskName = eventRecord.Task == null | eventRecord.Task == 0 ? "None" : $"({eventRecord.Task})"; - } - - description = providerDetails.Messages?.FirstOrDefault(m => m.ShortId == eventRecord.Id)?.Text; - description = FormatDescription(eventRecord, description); } return new DisplayEventModel( diff --git a/src/EventLogExpert.Library/Providers/EventMessageProvider.cs b/src/EventLogExpert.Library/Providers/EventMessageProvider.cs index 577bba7f..d330bf50 100644 --- a/src/EventLogExpert.Library/Providers/EventMessageProvider.cs +++ b/src/EventLogExpert.Library/Providers/EventMessageProvider.cs @@ -43,8 +43,8 @@ public EventMessageProvider(string providerName, string computerName, Action(); provider.Events ??= new List(); provider.Keywords ??= new Dictionary(); - provider.Opcodes ??= new Dictionary(); - provider.Tasks ??= new Dictionary(); + provider.Opcodes ??= new Dictionary(); + provider.Tasks ??= new Dictionary(); return provider; } @@ -204,12 +204,6 @@ private ProviderDetails LoadMessagesFromModernProvider() return provider; } - if (providerMetadata.Id == Guid.Empty) - { - _traceAction($"Provider {_providerName} has no provider GUID. Returning empty provider."); - return provider; - } - try { provider.Events = providerMetadata.Events.Select(e => new EventModel @@ -249,12 +243,12 @@ private ProviderDetails LoadMessagesFromModernProvider() try { provider.Opcodes = providerMetadata.Opcodes - .Select(i => new KeyValuePair(i.Value, i.DisplayName ?? i.Name)) + .Select(i => new KeyValuePair(i.Value, i.DisplayName ?? i.Name)) .ToDictionary(p => p.Key, p => p.Value); } catch (Exception ex) { - provider.Opcodes = new Dictionary(); + provider.Opcodes = new Dictionary(); _traceAction($"Failed to load Opcodes for modern provider: {_providerName}. Exception:"); _traceAction(ex.ToString()); } @@ -262,12 +256,12 @@ private ProviderDetails LoadMessagesFromModernProvider() try { provider.Tasks = providerMetadata.Tasks - .Select(i => new KeyValuePair(i.Value, i.DisplayName ?? i.Name)) + .Select(i => new KeyValuePair(i.Value, i.DisplayName ?? i.Name)) .ToDictionary(p => p.Key, p => p.Value); } catch (Exception ex) { - provider.Tasks = new Dictionary(); + provider.Tasks = new Dictionary(); _traceAction($"Failed to load Tasks for modern provider: {_providerName}. Exception:"); _traceAction(ex.ToString()); } diff --git a/src/EventLogExpert.Library/Providers/ProviderDetails.cs b/src/EventLogExpert.Library/Providers/ProviderDetails.cs index f980b99b..bba8d98e 100644 --- a/src/EventLogExpert.Library/Providers/ProviderDetails.cs +++ b/src/EventLogExpert.Library/Providers/ProviderDetails.cs @@ -17,7 +17,7 @@ public class ProviderDetails public Dictionary Keywords { get; set; } - public Dictionary Opcodes { get; set; } + public Dictionary Opcodes { get; set; } - public Dictionary Tasks { get; set; } + public Dictionary Tasks { get; set; } } diff --git a/src/EventLogExpert.Test/EventLogExpert.Test.csproj b/src/EventLogExpert.Test/EventLogExpert.Test.csproj index eacd1ed1..827ab343 100644 --- a/src/EventLogExpert.Test/EventLogExpert.Test.csproj +++ b/src/EventLogExpert.Test/EventLogExpert.Test.csproj @@ -9,13 +9,14 @@ - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/EventLogExpert.Test/UnitTest1.cs b/src/EventLogExpert.Test/UnitTest1.cs index d4bc2d26..3f6a9335 100644 --- a/src/EventLogExpert.Test/UnitTest1.cs +++ b/src/EventLogExpert.Test/UnitTest1.cs @@ -2,14 +2,34 @@ // // Licensed under the MIT License. using EventLogExpert.Library.EventResolvers; +using EventLogExpert.Library.Models; +using EventLogExpert.Library.Providers; +using Moq; using System.Diagnostics; using System.Diagnostics.Eventing.Reader; +using System.Reflection; +using System.Security.Principal; using Xunit.Abstractions; namespace EventLogExpert.Test; public class UnitTest1 { + internal class UnitTestEventResolver : EventResolverBase, IEventResolver + { + private readonly List _providerDetailsList; + + internal UnitTestEventResolver(List providerDetailsList) : base(s => Debug.WriteLine(s)) + { + _providerDetailsList = providerDetailsList; + } + + public DisplayEventModel Resolve(EventRecord eventRecord) + { + return ResolveFromProviderDetails(eventRecord, _providerDetailsList[0]); + } + } + private readonly ITestOutputHelper _outputHelper; public UnitTest1(ITestOutputHelper outputHelper) @@ -17,6 +37,75 @@ public UnitTest1(ITestOutputHelper outputHelper) _outputHelper = outputHelper; } + [Fact] + public void CanResolveMSExchangeRepl4114() + { + // This event has a message in the legacy provider, but a task in the modern provider. + + List properties = new() { "SERVER1", "4", "Lots of copy status text", "False" }; + var propList = new List(); + var constructors = typeof(EventProperty).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var p in properties) + { + var eventProperty = (EventProperty)constructors[0].Invoke(new[] { p }); + propList.Add(eventProperty); + } + + var eventRecord = new Mock(); + eventRecord.Setup(e => e.Id).Returns(4114); + eventRecord.Setup(e => e.Keywords).Returns(36028797018963968); + eventRecord.Setup(e => e.Level).Returns(4); + eventRecord.Setup(e => e.LogName).Returns("Application"); + eventRecord.Setup(e => e.Opcode).Returns(null); + eventRecord.Setup(e => e.Properties).Returns(propList); + eventRecord.Setup(e => e.ProviderId).Returns(null); + eventRecord.Setup(e => e.ProviderName).Returns("MSExchangeRepl"); + eventRecord.Setup(e => e.Qualifiers).Returns(16388); + eventRecord.Setup(e => e.RecordId).Returns(9518530); + eventRecord.Setup(e => e.RelatedActivityId).Returns(null); + eventRecord.Setup(e => e.Task).Returns(1); + eventRecord.Setup(e => e.TimeCreated).Returns(DateTime.Parse("1/7/2023 10:02:00 AM")); + eventRecord.Setup(e => e.UserId).Returns(null); + eventRecord.Setup(e => e.Version).Returns(null); + + var providerDetails = new ProviderDetails + { + Events = new List(), + Keywords = new Dictionary(), + Messages = new List + { + new MessageModel + { + LogLink = null, + ProviderName = "MSExchangeRepl", + RawId = 1074008082, + ShortId = 4114, + Tag = null, + Template = null, + Text = "Database redundancy health check passed.%nDatabase copy: %1%nRedundancy count: %2%nIsSuppressed: %4%n%nErrors:%n%3\r\n" + } + }, + Opcodes = new Dictionary(), + ProviderName = "MSExchangeRepl", + Tasks = new Dictionary + { + { 1, "Service" }, + { 2, "Exchange VSS Writer" }, + { 3, "Move" }, + { 4, "Upgrade" }, + { 5, "Action" }, + { 6, "ExRes" } + } + }; + + var resolver = new UnitTestEventResolver(new List { providerDetails }); + var result = resolver.Resolve(eventRecord.Object); + + var expectedDescription = "Database redundancy health check passed.\r\nDatabase copy: SERVER1\r\nRedundancy count: 4\r\nIsSuppressed: False\r\n\r\nErrors:\r\nLots of copy status text"; + Assert.Equal(expectedDescription, result.Description); + Assert.Equal("Service", result.TaskDisplayName); + } + [Fact] public void Test1() { @@ -57,6 +146,11 @@ public void Test1() totalCount++; } + foreach (var resolver in resolvers) + { + (resolver as IDisposable)?.Dispose(); + } + Assert.Equal(0, mismatchCount); }