Skip to content

Commit c9786f0

Browse files
committed
Refactored resolver and added support for Locale Metadata folders
1 parent ae2b810 commit c9786f0

File tree

12 files changed

+1982
-290
lines changed

12 files changed

+1982
-290
lines changed

src/EventLogExpert.Eventing.Tests/EventResolvers/EventResolverBaseTests.cs

Lines changed: 1148 additions & 102 deletions
Large diffs are not rendered by default.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// // Copyright (c) Microsoft Corporation.
2+
// // Licensed under the MIT License.
3+
4+
using EventLogExpert.Eventing.Helpers;
5+
6+
namespace EventLogExpert.Eventing.Tests.Helpers;
7+
8+
public sealed class NativeMethodsTests
9+
{
10+
[Theory]
11+
[InlineData("%1 parameter", true)]
12+
[InlineData("%9 parameter", true)]
13+
[InlineData("error %1 occurred", true)]
14+
[InlineData("code %10 detail", true)]
15+
[InlineData("code %99 detail", true)]
16+
[InlineData("%1!s! insert", true)]
17+
[InlineData("value: %s", true)]
18+
[InlineData("value: %S", true)]
19+
[InlineData("count: %d", true)]
20+
[InlineData("count: %i", true)]
21+
[InlineData("count: %u", true)]
22+
[InlineData("hex: %x", true)]
23+
[InlineData("hex: %X", true)]
24+
[InlineData("ptr: %p", true)]
25+
[InlineData("char: %c", true)]
26+
[InlineData("char: %C", true)]
27+
[InlineData("octal: %o", true)]
28+
[InlineData("float: %f", true)]
29+
[InlineData("float: %F", true)]
30+
[InlineData("sci: %e", true)]
31+
[InlineData("sci: %E", true)]
32+
[InlineData("gen: %g", true)]
33+
[InlineData("gen: %G", true)]
34+
[InlineData("100%% complete", false)]
35+
[InlineData("clean message", false)]
36+
[InlineData("100% done", false)]
37+
[InlineData("value %0", false)]
38+
[InlineData("trailing %", false)]
39+
[InlineData("", false)]
40+
[InlineData("%%s escaped", false)]
41+
[InlineData("a %% b %% c", false)]
42+
public void ContainsFormatInsert_DetectsExpectedPatterns(string input, bool expected) =>
43+
Assert.Equal(expected, NativeMethods.ContainsFormatInsert(input));
44+
}

src/EventLogExpert.Eventing.Tests/Readers/EventLogReaderTests.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,14 @@ public void TryGetEvents_WhenLargeLogWithManyReads_ShouldEventuallyReturnFalse()
492492
// Arrange
493493
using var reader = new EventLogReader(Constants.ApplicationLogName, PathType.LogName);
494494

495-
int maxIterations = 1000; // Safety limit
496-
int iterations = 0;
495+
var logInfo = new EventLogInformation(
496+
EventLogSession.GlobalSession,
497+
Constants.ApplicationLogName,
498+
PathType.LogName);
499+
500+
// Derive iteration limit from actual log size (batch size defaults to 30)
501+
long maxIterations = (logInfo.RecordCount ?? 0) / 30 + 100;
502+
long iterations = 0;
497503

498504
// Act
499505
bool success;

src/EventLogExpert.Eventing/EventResolvers/EventResolver.cs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using EventLogExpert.Eventing.Models;
77
using EventLogExpert.Eventing.Providers;
88
using Microsoft.EntityFrameworkCore;
9+
using System.Collections.Concurrent;
910
using System.Collections.Immutable;
1011
using System.Text.RegularExpressions;
1112

@@ -20,9 +21,11 @@ namespace EventLogExpert.Eventing.EventResolvers;
2021
public sealed partial class EventResolver : EventResolverBase, IEventResolver
2122
{
2223
private readonly Lock _databaseAccessLock = new();
24+
private readonly ConcurrentDictionary<string, bool> _providerFromNonLocal = new(StringComparer.OrdinalIgnoreCase);
25+
private readonly ConcurrentDictionary<string, ProviderDetails?> _supplementalDetails = new(StringComparer.OrdinalIgnoreCase);
2326

2427
private ImmutableArray<EventProviderDbContext> _dbContexts = [];
25-
private IReadOnlyList<string>? _metadataPaths;
28+
private ImmutableArray<string> _metadataPaths = [];
2629

2730
public EventResolver(
2831
IDatabaseCollectionProvider? dbCollection = null,
@@ -37,6 +40,13 @@ public EventResolver(
3740
Logger?.Debug($"{nameof(EventResolver)} was instantiated.");
3841
}
3942

43+
public override DisplayEventModel ResolveEvent(EventRecord eventRecord)
44+
{
45+
ResolveProviderDetails(eventRecord);
46+
47+
return base.ResolveEvent(eventRecord);
48+
}
49+
4050
public void ResolveProviderDetails(EventRecord eventRecord)
4151
{
4252
ObjectDisposedException.ThrowIf(IsDisposed, nameof(EventResolver));
@@ -49,7 +59,7 @@ public void ResolveProviderDetails(EventRecord eventRecord)
4959
if (ProviderDetails.ContainsKey(eventRecord.ProviderName)) { return; }
5060

5161
// 1. Try MTA locale metadata files (primary source for exported logs)
52-
if (_metadataPaths is { Count: > 0 } && TryResolveFromMta(eventRecord))
62+
if (_metadataPaths.Length > 0 && TryResolveFromMta(eventRecord))
5363
{
5464
return;
5565
}
@@ -65,11 +75,7 @@ public void ResolveProviderDetails(EventRecord eventRecord)
6575
}
6676
}
6777

68-
/// <summary>
69-
/// Sets the MTA locale metadata file paths for exported log resolution. Must be called before any events are
70-
/// resolved.
71-
/// </summary>
72-
public void SetMetadataPaths(IReadOnlyList<string> metadataPaths) => _metadataPaths = metadataPaths;
78+
public void SetMetadataPaths(IReadOnlyList<string> metadataPaths) => _metadataPaths = [.. metadataPaths];
7379

7480
/// <summary>
7581
/// If the database file name ends in a year or a number, such as Exchange 2019 or Windows 2016, we want to sort
@@ -144,6 +150,21 @@ protected override void Dispose(bool disposing)
144150
base.Dispose(disposing);
145151
}
146152

153+
protected override ProviderDetails? TryGetSupplementalDetails(EventRecord eventRecord)
154+
{
155+
// Only supplement providers that were loaded from MTA/DB
156+
if (!_providerFromNonLocal.ContainsKey(eventRecord.ProviderName)) { return null; }
157+
158+
return _supplementalDetails.GetOrAdd(
159+
eventRecord.ProviderName,
160+
name =>
161+
{
162+
Logger?.Debug($"Loading supplemental local provider for {name}");
163+
164+
return new EventMessageProvider(name, Logger).LoadProviderDetails();
165+
});
166+
}
167+
147168
[GeneratedRegex("^(.+) (\\S+)$")]
148169
private static partial Regex SplitProductAndVersionRegex();
149170

@@ -228,6 +249,7 @@ private bool TryResolveFromDatabase(EventRecord eventRecord)
228249

229250
Logger?.Debug($"Resolved {eventRecord.ProviderName} from database {dbContext.Name}.");
230251
ProviderDetails.TryAdd(eventRecord.ProviderName, details);
252+
_providerFromNonLocal.TryAdd(eventRecord.ProviderName, true);
231253

232254
return true;
233255
}
@@ -244,12 +266,13 @@ private bool TryResolveFromMta(EventRecord eventRecord)
244266
_metadataPaths,
245267
Logger).LoadProviderDetails();
246268

247-
if (details.Events.Count == 0 && details.Keywords.Count == 0)
269+
if (details.Events.Count == 0 && details.Keywords.Count == 0 && details.Messages.Count == 0)
248270
{
249271
return false;
250272
}
251273

252274
ProviderDetails.TryAdd(eventRecord.ProviderName, details);
275+
_providerFromNonLocal.TryAdd(eventRecord.ProviderName, true);
253276

254277
return true;
255278
}

0 commit comments

Comments
 (0)