Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/EventLogExpert.Eventing.Tests/Helpers/LogNamesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Helpers;

namespace EventLogExpert.Eventing.Tests.Helpers;

public sealed class LogNamesTests
{
[Fact]
public void AdminOnlyLiveLogNames_ContainsExpectedNames()
{
Assert.Contains(LogNames.SecurityLog, LogNames.AdminOnlyLiveLogNames);
Assert.Contains(LogNames.StateLog, LogNames.AdminOnlyLiveLogNames);
Assert.Equal(2, LogNames.AdminOnlyLiveLogNames.Count);
}

[Theory]
[InlineData("security")]
[InlineData("SECURITY")]
[InlineData("state")]
[InlineData("STATE")]
public void AdminOnlyLiveLogNames_ShouldMatchCaseInsensitively(string input)
{
Assert.Contains(input, LogNames.AdminOnlyLiveLogNames);
}

[Fact]
public void Constants_HaveExpectedValues()
{
Assert.Equal("Application", LogNames.ApplicationLog);
Assert.Equal("Security", LogNames.SecurityLog);
Assert.Equal("State", LogNames.StateLog);
Assert.Equal("System", LogNames.SystemLog);
}
}
19 changes: 19 additions & 0 deletions src/EventLogExpert.Eventing/Helpers/LogNames.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

namespace EventLogExpert.Eventing.Helpers;

public static class LogNames
{
public const string ApplicationLog = "Application";
public const string SecurityLog = "Security";
public const string StateLog = "State";
public const string SystemLog = "System";

/// <summary>Live event log names that require process elevation to read.</summary>
public static IReadOnlySet<string> AdminOnlyLiveLogNames { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
SecurityLog,
StateLog,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public IEnumerable<string> GetMessageFilesForLegacyProvider(string providerName)
foreach (var logSubKeyName in eventLogKey.GetSubKeyNames())
{
// Skip Security and State since it requires elevation
if (logSubKeyName is "Security" or "State")
if (LogNames.AdminOnlyLiveLogNames.Contains(logSubKeyName))
{
continue;
}
Expand Down
18 changes: 18 additions & 0 deletions src/EventLogExpert.UI.Tests/LogNameMethodsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,22 @@ public void GetMenuPath_WhenSimpleRootLog_ShouldReturnSingleSegment()

Assert.Equal(["Application"], path);
}

[Fact]
public void HardCodedLiveLogNames_ContainsExpectedNames()
{
Assert.Contains("Application", LogNameMethods.HardCodedLiveLogNames);
Assert.Contains("System", LogNameMethods.HardCodedLiveLogNames);
Assert.Contains("Security", LogNameMethods.HardCodedLiveLogNames);
Assert.Equal(3, LogNameMethods.HardCodedLiveLogNames.Count);
}

[Theory]
[InlineData("application")]
[InlineData("APPLICATION")]
[InlineData("Application")]
public void HardCodedLiveLogNames_ShouldMatchCaseInsensitively(string input)
{
Assert.Contains(input, LogNameMethods.HardCodedLiveLogNames);
}
}
6 changes: 3 additions & 3 deletions src/EventLogExpert.UI/Interfaces/IMenuActionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ public interface IMenuActionService

Task OpenDocsAsync();

Task OpenFileAsync(bool addLog);
Task OpenFileAsync(bool combineLog);

Task OpenFolderAsync(bool addLog);
Task OpenFolderAsync(bool combineLog);

Task OpenIssueAsync();

Task OpenLiveLogAsync(string logName, bool addLog);
Task OpenLiveLogAsync(string logName, bool combineLog);

Task OpenSettingsAsync();

Expand Down
10 changes: 10 additions & 0 deletions src/EventLogExpert.UI/LogNameMethods.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Helpers;

namespace EventLogExpert.UI;

public static class LogNameMethods
{
private const string MicrosoftWindowsPrefix = "Microsoft-Windows-";

/// <summary>Live event log names hard-coded in MenuBar's File menu — filter these from dynamic log enumeration to avoid duplicates.</summary>
public static IReadOnlySet<string> HardCodedLiveLogNames { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
LogNames.ApplicationLog,
LogNames.SystemLog,
LogNames.SecurityLog,
};

public static IReadOnlyList<string> GetMenuPath(string logName)
{
if (string.IsNullOrWhiteSpace(logName))
Expand Down
17 changes: 9 additions & 8 deletions src/EventLogExpert/Services/MauiMenuActionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public async Task<IReadOnlyList<string>> GetOtherLogNamesAsync()

_cachedLogNames = await Task.Run<IReadOnlyList<string>>(() =>
EventLogSession.GlobalSession.GetLogNames()
.Where(name => !LogNameMethods.HardCodedLiveLogNames.Contains(name))
.OrderBy(name => name, StringComparer.OrdinalIgnoreCase)
.ToList());

Expand All @@ -130,7 +131,7 @@ public async Task<IReadOnlyList<string>> GetOtherLogNamesAsync()
public Task OpenDocsAsync() =>
OpenBrowserAsync("https://github.com/microsoft/EventLogExpert/blob/main/docs/Home.md");

public async Task OpenFileAsync(bool addLog)
public async Task OpenFileAsync(bool combineLog)
{
var options = new PickOptions
{
Expand All @@ -142,7 +143,7 @@ public async Task OpenFileAsync(bool addLog)

if (!files.Any()) { return; }

if (!addLog)
if (!combineLog)
{
await CloseAllLogsAsync();
}
Expand All @@ -155,7 +156,7 @@ public async Task OpenFileAsync(bool addLog)
}
}

public async Task OpenFolderAsync(bool addLog)
public async Task OpenFolderAsync(bool combineLog)
{
string? folderPath = await FolderPickerHelper.PickFolderAsync();

Expand All @@ -165,7 +166,7 @@ public async Task OpenFolderAsync(bool addLog)

if (files.Count == 0) { return; }

if (!addLog)
if (!combineLog)
{
await CloseAllLogsAsync();
}
Expand All @@ -178,13 +179,13 @@ public async Task OpenFolderAsync(bool addLog)

public Task OpenIssueAsync() => OpenBrowserAsync("https://github.com/microsoft/EventLogExpert/issues/new");

public Task OpenLiveLogAsync(string logName, bool addLog) => OpenLogAsync(logName, PathType.LogName, addLog);
public Task OpenLiveLogAsync(string logName, bool combineLog) => OpenLogAsync(logName, PathType.LogName, combineLog);

public async Task OpenLogAsync(string logPath, PathType pathType, bool shouldAddLog = false)
public async Task OpenLogAsync(string logPath, PathType pathType, bool combineLog = false)
{
if (string.IsNullOrWhiteSpace(logPath)) { return; }

if (shouldAddLog && _eventLogState.Value.ActiveLogs.ContainsKey(logPath)) { return; }
if (combineLog && _eventLogState.Value.ActiveLogs.ContainsKey(logPath)) { return; }

EventLogInformation? eventLogInformation;

Expand Down Expand Up @@ -214,7 +215,7 @@ await _dialogService.ShowAlert(
return;
}

if (!shouldAddLog)
if (!combineLog)
{
await _cancellationTokenSource.CancelAsync();
_dispatcher.Dispatch(new EventLogAction.CloseAll());
Expand Down
72 changes: 46 additions & 26 deletions src/EventLogExpert/Shared/Components/Menu/MenuBar.razor.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Helpers;
using EventLogExpert.UI;
using EventLogExpert.UI.Interfaces;
using EventLogExpert.UI.Services;
using EventLogExpert.UI.Store.EventLog;
using EventLogExpert.UI.Store.FilterPane;
using Fluxor;
Expand All @@ -16,6 +18,7 @@ namespace EventLogExpert.Shared.Components.Menu;
public sealed partial class MenuBar : IDisposable
{
private readonly List<TopLevel> _bars = [];

private ElementReference[] _barElements = [];
private int _focusedBarIndex;
private long _openRequestId;
Expand All @@ -27,9 +30,14 @@ public sealed partial class MenuBar : IDisposable
[Inject]
private IStateSelection<EventLogState, bool> ContinuouslyUpdate { get; init; } = null!;

[Inject] private ICurrentVersionProvider CurrentVersionProvider { get; init; } = null!;

[Inject]
private IStateSelection<FilterPaneState, bool> FilterPaneIsEnabled { get; init; } = null!;

[Inject]
private IStateSelection<EventLogState, bool> HasActiveLogs { get; init; } = null!;

[Inject] private IJSRuntime JSRuntime { get; init; } = null!;

[Inject] private IMenuService MenuService { get; init; } = null!;
Expand All @@ -49,6 +57,7 @@ protected override void OnInitialized()
{
ContinuouslyUpdate.Select(state => state.ContinuouslyUpdate);
FilterPaneIsEnabled.Select(state => state.IsEnabled);
HasActiveLogs.Select(state => !state.ActiveLogs.IsEmpty);

Settings.CopyTypeChanged += OnSettingsChanged;
MenuService.StateChanged += OnMenuServiceStateChanged;
Expand Down Expand Up @@ -86,14 +95,19 @@ private IReadOnlyList<MenuItem> BuildEdit()
];
}

private IReadOnlyList<MenuItem> BuildFile() =>
[
MenuItem.SubMenu("Open", BuildOpenSubMenu(false)),
MenuItem.SubMenu("Add Another Log To This View", BuildOpenSubMenu(true)),
MenuItem.Separator(),
MenuItem.Item("Close All Open Logs", () => Actions.CloseAllLogsAsync()),
MenuItem.Item("Exit", Actions.Exit),
];
private IReadOnlyList<MenuItem> BuildFile()
{
bool hasActiveLogs = HasActiveLogs.Value;

return
[
MenuItem.SubMenu("Open", BuildOpenSubMenu(false)),
MenuItem.SubMenu("Combine", BuildOpenSubMenu(true), isEnabled: hasActiveLogs),
MenuItem.Separator(),
MenuItem.Item("Close All", () => Actions.CloseAllLogsAsync(), isEnabled: hasActiveLogs),
MenuItem.Item("Exit", Actions.Exit),
];
}

private IReadOnlyList<MenuItem> BuildHelp() =>
[
Expand All @@ -104,22 +118,27 @@ private IReadOnlyList<MenuItem> BuildHelp() =>
MenuItem.Item("View Logs", () => Actions.ShowDebugLogsAsync()),
];

private IReadOnlyList<MenuItem> BuildOpenSubMenu(bool addLog) =>
[
MenuItem.Item("File", () => Actions.OpenFileAsync(addLog), addLog ? null : "Ctrl+O"),
MenuItem.Item("Folder", () => Actions.OpenFolderAsync(addLog)),
MenuItem.SubMenu("Live Event Log",
private IReadOnlyList<MenuItem> BuildOpenSubMenu(bool combineLog)
{
bool isAdmin = CurrentVersionProvider.IsAdmin;

return
[
MenuItem.Item("Application", () => Actions.OpenLiveLogAsync("Application", addLog)),
MenuItem.Item("System", () => Actions.OpenLiveLogAsync("System", addLog)),
MenuItem.Item("Security", () => Actions.OpenLiveLogAsync("Security", addLog)),
MenuItem.AsyncSubMenu(
"Other Logs",
async () => BuildOtherLogsTree(await Actions.GetOtherLogNamesAsync(), addLog)),
]),
];
MenuItem.Item("File", () => Actions.OpenFileAsync(combineLog), combineLog ? null : "Ctrl+O"),
MenuItem.Item("Folder", () => Actions.OpenFolderAsync(combineLog)),
Comment thread
jschick04 marked this conversation as resolved.
MenuItem.SubMenu("Live",
[
MenuItem.Item(LogNames.ApplicationLog, () => Actions.OpenLiveLogAsync(LogNames.ApplicationLog, combineLog)),
MenuItem.Item(LogNames.SystemLog, () => Actions.OpenLiveLogAsync(LogNames.SystemLog, combineLog)),
MenuItem.Item(LogNames.SecurityLog, () => Actions.OpenLiveLogAsync(LogNames.SecurityLog, combineLog), isEnabled: isAdmin),
MenuItem.AsyncSubMenu(
"Other Logs",
async () => BuildOtherLogsTree(await Actions.GetOtherLogNamesAsync(), combineLog, isAdmin)),
]),
];
}

private IReadOnlyList<MenuItem> BuildOtherLogsTree(IReadOnlyList<string> logNames, bool addLog)
private IReadOnlyList<MenuItem> BuildOtherLogsTree(IReadOnlyList<string> logNames, bool combineLog, bool isAdmin)
{
var rootChildren = new List<MenuItem>();
var folderMap = new Dictionary<string, List<MenuItem>>(StringComparer.OrdinalIgnoreCase);
Expand All @@ -130,12 +149,13 @@ private IReadOnlyList<MenuItem> BuildOtherLogsTree(IReadOnlyList<string> logName

if (path.Count == 0) { continue; }

var leafLabel = path[^1];
var leaf = MenuItem.Item(leafLabel, () => Actions.OpenLiveLogAsync(logName, addLog));
var log = path[^1];
var logIsEnabled = isAdmin || !LogNames.AdminOnlyLiveLogNames.Contains(logName);
var logMenuItem = MenuItem.Item(log, () => Actions.OpenLiveLogAsync(logName, combineLog), isEnabled: logIsEnabled);

if (path.Count == 1)
{
rootChildren.Add(leaf);
rootChildren.Add(logMenuItem);

continue;
}
Expand All @@ -161,7 +181,7 @@ private IReadOnlyList<MenuItem> BuildOtherLogsTree(IReadOnlyList<string> logName
children = newChildren;
}

children.Add(leaf);
children.Add(logMenuItem);
}

return rootChildren;
Expand Down
Loading