Skip to content

Commit

Permalink
Moved caching to resolver instead of log data for multi log improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jschick04 committed Oct 24, 2024
1 parent 0eca2a4 commit 1b47d8d
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 42 deletions.
47 changes: 29 additions & 18 deletions src/EventLogExpert.Eventing/EventResolvers/EventResolverBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ public partial class EventResolverBase : IDisposable
/// The mappings from the outType attribute in the EventModel XML template to determine if it should be displayed
/// as Hex.
/// </summary>
private static readonly List<string> DisplayAsHexTypes =
private static readonly List<string> s_displayAsHexTypes =
[
"win:HexInt32",
"win:HexInt64",
"win:Pointer",
"win:Win32Error"
];

private static readonly ConcurrentDictionary<string, string[]> FormattedPropertiesCache = [];
private static readonly ConcurrentDictionary<string, string[]> s_formattedPropertiesCache = [];

/// <summary>
/// These are already defined in System.Diagnostics.Eventing.Reader.StandardEventKeywords. However, the names
/// there do not match what is normally displayed in Event Viewer. We redefine them here so we can use our own strings.
/// </summary>
private static readonly Dictionary<long, string> StandardKeywords = new()
private static readonly Dictionary<long, string> s_standardKeywords = new()
{
{ 0x1000000000000, "Response Time" },
{ 0x2000000000000, "Wdi Context" },
Expand All @@ -46,7 +46,8 @@ public partial class EventResolverBase : IDisposable
{ 0x80000000000000, "Classic" }
};

private static readonly StringCache XmlCache = new();
private static readonly StringCache s_descriptionCache = new();
private static readonly StringCache s_xmlCache = new();

protected readonly ConcurrentDictionary<string, ProviderDetails?> providerDetails = new();
protected readonly Action<string, LogLevel> tracer;
Expand All @@ -72,9 +73,14 @@ public IEnumerable<string> GetKeywordsFromBitmask(EventRecord eventRecord)

List<string> returnValue = [];

foreach (var k in StandardKeywords.Keys)
foreach (var k in s_standardKeywords.Keys)
{
if ((eventRecord.Keywords.Value & k) == k) { returnValue.Add(StandardKeywords[k].TrimEnd('\0')); }
if ((eventRecord.Keywords.Value & k) == k)
{
returnValue.Add(
IEventResolver.ValueCache.Get(
s_standardKeywords[k].TrimEnd('\0')));
}
}

if (!providerDetails.TryGetValue(eventRecord.ProviderName, out var details) || details is null)
Expand All @@ -90,7 +96,12 @@ public IEnumerable<string> GetKeywordsFromBitmask(EventRecord eventRecord)
{
foreach (var k in details.Keywords.Keys)
{
if ((lower32 & k) == k) { returnValue.Add(details.Keywords[k].TrimEnd('\0')); }
if ((lower32 & k) == k)
{
returnValue.Add(
IEventResolver.ValueCache.Get(
details.Keywords[k].TrimEnd('\0')));
}
}
}

Expand Down Expand Up @@ -128,7 +139,7 @@ public string ResolveTaskName(EventRecord eventRecord)

if (@event?.Task is not null && details.Tasks.TryGetValue(@event.Task, out var taskName))
{
return taskName.TrimEnd('\0');
return IEventResolver.ValueCache.Get(taskName.TrimEnd('\0'));
}

if (!eventRecord.Task.HasValue)
Expand All @@ -140,7 +151,7 @@ public string ResolveTaskName(EventRecord eventRecord)

if (taskName is not null)
{
return taskName.TrimEnd('\0');
return IEventResolver.ValueCache.Get(taskName.TrimEnd('\0'));
}

var potentialTaskNames = details.Messages
Expand All @@ -165,7 +176,7 @@ public string ResolveTaskName(EventRecord eventRecord)
taskName = (eventRecord.Task == null) | (eventRecord.Task == 0) ? "None" : $"({eventRecord.Task})";
}

return taskName.TrimEnd('\0');
return IEventResolver.ValueCache.Get(taskName.TrimEnd('\0'));
}

protected virtual void Dispose(bool disposing)
Expand All @@ -174,7 +185,7 @@ protected virtual void Dispose(bool disposing)

if (disposing)
{
FormattedPropertiesCache.Clear();
s_formattedPropertiesCache.Clear();
providerDetails.Clear();
}

Expand All @@ -192,7 +203,7 @@ protected string FormatDescription(
// when the entire description is a string literal, and there is no provider DLL needed.
// Found a few providers that have their properties wrapped with \r\n for some reason
return properties.Count == 1 ?
properties[0].Trim(['\0', '\r', '\n']) :
s_descriptionCache.Get(properties[0].Trim(['\0', '\r', '\n'])) :
"Unable to resolve description, see XML for more details.";
}

Expand Down Expand Up @@ -256,7 +267,7 @@ protected string FormatDescription(
updatedDescription.Append(description[lastIndex..].TrimEnd(['\0', '\r', '\n']));
}

return updatedDescription.ToString();
return s_descriptionCache.Get(updatedDescription.ToString());
}
catch (InvalidOperationException)
{
Expand All @@ -278,9 +289,9 @@ private static List<string> GetFormattedProperties(string? template, IList<Event

if (!string.IsNullOrWhiteSpace(template))
{
template = XmlCache.Get(template);
template = s_xmlCache.Get(template);

if (FormattedPropertiesCache.TryGetValue(template, out var values))
if (s_formattedPropertiesCache.TryGetValue(template, out var values))
{
dataNodes = values;
}
Expand All @@ -290,10 +301,10 @@ private static List<string> GetFormattedProperties(string? template, IList<Event
.Descendants()
.Attributes()
.Where(a => a.Name == "outType")
.Select(a => XmlCache.Get(a.Value))
.Select(a => s_xmlCache.Get(a.Value))
.ToArray();

FormattedPropertiesCache.TryAdd(template, dataNodes);
s_formattedPropertiesCache.TryAdd(template, dataNodes);
}
}

Expand Down Expand Up @@ -322,7 +333,7 @@ private static List<string> GetFormattedProperties(string? template, IList<Event
continue;
}

if (DisplayAsHexTypes.Contains(outType, StringComparer.OrdinalIgnoreCase))
if (s_displayAsHexTypes.Contains(outType, StringComparer.OrdinalIgnoreCase))
{
providers.Add($"0x{properties[i].Value:X}");
}
Expand Down
3 changes: 3 additions & 0 deletions src/EventLogExpert.Eventing/EventResolvers/IEventResolver.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Models;
using System.Diagnostics.Eventing.Reader;

namespace EventLogExpert.Eventing.EventResolvers;
Expand All @@ -11,6 +12,8 @@ namespace EventLogExpert.Eventing.EventResolvers;
/// </summary>
public interface IEventResolver : IDisposable
{
public static readonly StringCache ValueCache = new();

public IEnumerable<string> GetKeywordsFromBitmask(EventRecord eventRecord);

public string ResolveDescription(EventRecord eventRecord);
Expand Down
2 changes: 1 addition & 1 deletion src/EventLogExpert.Eventing/Models/DisplayEventModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed record DisplayEventModel(

public int Id { get; init; }

public List<string> KeywordsDisplayNames { get; init; } = [];
public IEnumerable<string> KeywordsDisplayNames { get; init; } = [];

public string Level { get; init; } = string.Empty;

Expand Down
23 changes: 7 additions & 16 deletions src/EventLogExpert.UI/Models/EventLogData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,27 @@ public readonly record struct EventLogData(
{
public EventLogId Id { get; } = EventLogId.Create();

public StringCache ValueCache { get; } = new();

public List<string> GetCategoryValues(FilterCategory category)
public IEnumerable<string> GetCategoryValues(FilterCategory category)
{
switch (category)
{
case FilterCategory.Id:
return Events.Select(e => e.Id.ToString())
.Distinct().Order().ToList();
.Distinct().Order();
case FilterCategory.ActivityId:
return Events.Select(e => e.ActivityId?.ToString() ?? string.Empty)
.Distinct().Order().ToList();
.Distinct().Order();
case FilterCategory.Level:
List<string> items = [];

foreach (SeverityLevel item in Enum.GetValues(typeof(SeverityLevel)))
{
items.Add(item.ToString());
}

return items;
return Enum.GetNames<SeverityLevel>();
case FilterCategory.KeywordsDisplayNames:
return Events.SelectMany(e => e.KeywordsDisplayNames)
.Distinct().Order().ToList();
.Distinct().Order();
case FilterCategory.Source:
return Events.Select(e => e.Source)
.Distinct().Order().ToList();
.Distinct().Order();
case FilterCategory.TaskCategory:
return Events.Select(e => e.TaskCategory)
.Distinct().Order().ToList();
.Distinct().Order();
case FilterCategory.Xml:
case FilterCategory.Description:
default:
Expand Down
13 changes: 6 additions & 7 deletions src/EventLogExpert.UI/Store/EventLog/EventLogEffects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,16 @@ await Parallel.ForEachAsync(
new DisplayEventModel(action.LogName)
{
ActivityId = @event.ActivityId,
ComputerName = logData.ValueCache.Get(@event.MachineName),
Description = logData.ValueCache.Get(eventResolver.ResolveDescription(@event)),
ComputerName = IEventResolver.ValueCache.Get(@event.MachineName),
Description = eventResolver.ResolveDescription(@event),
Id = @event.Id,
KeywordsDisplayNames = eventResolver.GetKeywordsFromBitmask(@event)
.Select(keyword => logData.ValueCache.Get(keyword)).ToList(),
KeywordsDisplayNames = eventResolver.GetKeywordsFromBitmask(@event),
Level = Severity.GetString(@event.Level),
LogName = logData.ValueCache.Get(@event.LogName),
LogName = IEventResolver.ValueCache.Get(@event.LogName),
ProcessId = @event.ProcessId,
RecordId = @event.RecordId,
Source = logData.ValueCache.Get(@event.ProviderName),
TaskCategory = logData.ValueCache.Get(eventResolver.ResolveTaskName(@event)),
Source = IEventResolver.ValueCache.Get(@event.ProviderName),
TaskCategory = eventResolver.ResolveTaskName(@event),
ThreadId = @event.ThreadId,
TimeCreated = @event.TimeCreated!.Value.ToUniversalTime(),
UserId = @event.UserId,
Expand Down

0 comments on commit 1b47d8d

Please sign in to comment.