Skip to content
Closed
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 .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Build

on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
build:
runs-on: windows-latest

steps:
- uses: actions/checkout@v4

- name: Setup .NET 6
uses: actions/setup-dotnet@v4
with:
dotnet-version: "6.0.x"

- name: Publish
run: >
dotnet publish csharp/ScalanceLogs.csproj
-c Release
-r win-x64
--self-contained false
-p:PublishSingleFile=true
-p:IncludeNativeLibrariesForSelfExtract=true
-o publish

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ScalanceLogs
path: publish/
retention-days: 30
2 changes: 1 addition & 1 deletion csharp/Helpers/IconHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private static Bitmap DrawBrand(int size)

// ── Dots (3 circles, r=4, y=62, spaced 17px apart) ────────────────────
float dr = 4 * s, dy = 62 * s;
float[] dotX = [14 * s, 31 * s, 48 * s];
float[] dotX = new float[] { 14 * s, 31 * s, 48 * s };
using var dotBrush = new SolidBrush(DotColor);
foreach (var dx in dotX)
g.FillEllipse(dotBrush, dx, dy, dr * 2, dr * 2);
Expand Down
19 changes: 10 additions & 9 deletions csharp/Helpers/PathValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ public static class PathValidator
"ScalanceLogs", "logs");

private static readonly string[] ForbiddenRoots =
[
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
Environment.GetFolderPath(Environment.SpecialFolder.System),
Environment.GetFolderPath(Environment.SpecialFolder.SystemX86),
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles),
Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86),
];
new string[]
{
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
Environment.GetFolderPath(Environment.SpecialFolder.System),
Environment.GetFolderPath(Environment.SpecialFolder.SystemX86),
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles),
Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86),
};

/// <summary>
/// Returns a safe absolute path. If the supplied path is empty, unsafe,
Expand Down
10 changes: 5 additions & 5 deletions csharp/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ namespace ScalanceLogs;

public partial class MainWindow : Window
{
private readonly ObservableCollection<LogEntry> _logEntries = [];
private readonly ObservableCollection<LiveEntry> _liveEntries = [];
private readonly ObservableCollection<string> _activeSw = [];
private readonly ObservableCollection<FileGroup> _fileGroups = [];
private readonly ObservableCollection<LogEntry> _logEntries = new ObservableCollection<LogEntry>();
private readonly ObservableCollection<LiveEntry> _liveEntries = new ObservableCollection<LiveEntry>();
private readonly ObservableCollection<string> _activeSw = new ObservableCollection<string>();
private readonly ObservableCollection<FileGroup> _fileGroups = new ObservableCollection<FileGroup>();

private string? _currentFile;
private string _activeQuickFilter = "";
Expand Down Expand Up @@ -106,7 +106,7 @@ private static List<FileGroup> GroupFiles(string[] files)
g.Switches.Add(new FileItem { Display = ip, FileName = f });
}
}
return [.. map.Values];
return map.Values.ToList();
}

private void FileTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
Expand Down
51 changes: 27 additions & 24 deletions csharp/Models/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,46 @@ public class AppSettings
public string Theme { get; set; } = "Cyber";
public bool BalloonNotifications { get; set; } = true;
// Labels that trigger balloon. Empty = ALL messages.
public List<string> BalloonLabels { get; set; } = ["EMERG", "ALERT", "CRIT", "ERROR",
"LINK DOWN", "LINK UP"];
public List<string> BalloonLabels { get; set; } = new List<string> { "EMERG", "ALERT", "CRIT", "ERROR",
"LINK DOWN", "LINK UP" };

// Per-theme color overrides (key = theme name, value = {brushKey → hex})
public Dictionary<string, Dictionary<string, string>> ThemeOverrides { get; set; } = new();

public List<SwitchNameEntry> SwitchNames { get; set; } = [];
public List<SwitchNameEntry> SwitchNames { get; set; } = new List<SwitchNameEntry>();
public List<string> EventPatterns { get; set; } = DefaultEventPatterns();
public List<MessageTypeEntry> MessageTypes { get; set; } = DefaultMessageTypes();
public List<QuickFilterEntry> QuickFilters { get; set; } = DefaultQuickFilters();

public static List<string> DefaultEventPatterns() =>
[
@"link\s+(up|down)",
@"port.*(up|down)",
@"(fault|error|fail)",
];
new List<string>
{
@"link\s+(up|down)",
@"port.*(up|down)",
@"(fault|error|fail)",
};

public static List<MessageTypeEntry> DefaultMessageTypes() =>
[
new() { Pattern = @"link\s+down", Label = "LINK DOWN", Color = "#ff6b6b", Bg = "rgba(255,107,107,0.15)" },
new() { Pattern = @"link\s+up", Label = "LINK UP", Color = "#4a9e72", Bg = "rgba(74,158,114,0.12)" },
new() { Pattern = @"time synchronized", Label = "NTP SYNC", Color = "#4fa8c5", Bg = "rgba(79,168,197,0.10)" },
new() { Pattern = @"time not synchronized", Label = "NTP LOST", Color = "#b8913a", Bg = "rgba(184,145,58,0.12)" },
new() { Pattern = @"configuration changed", Label = "CFG CHG", Color = "#b8913a", Bg = "rgba(184,145,58,0.10)" },
new() { Pattern = @"logged in", Label = "LOGIN", Color = "#a8b4cc", Bg = "rgba(168,180,204,0.08)" },
new() { Pattern = @"logged out|inactivity", Label = "LOGOUT", Color = "#556070", Bg = "rgba(85,96,112,0.10)" },
new() { Pattern = @"log file cleared", Label = "LOG CLR", Color = "#556070", Bg = "rgba(85,96,112,0.08)" },
];
new List<MessageTypeEntry>
{
new MessageTypeEntry { Pattern = @"link\s+down", Label = "LINK DOWN", Color = "#ff6b6b", Bg = "rgba(255,107,107,0.15)" },
new MessageTypeEntry { Pattern = @"link\s+up", Label = "LINK UP", Color = "#4a9e72", Bg = "rgba(74,158,114,0.12)" },
new MessageTypeEntry { Pattern = @"time synchronized", Label = "NTP SYNC", Color = "#4fa8c5", Bg = "rgba(79,168,197,0.10)" },
new MessageTypeEntry { Pattern = @"time not synchronized", Label = "NTP LOST", Color = "#b8913a", Bg = "rgba(184,145,58,0.12)" },
new MessageTypeEntry { Pattern = @"configuration changed", Label = "CFG CHG", Color = "#b8913a", Bg = "rgba(184,145,58,0.10)" },
new MessageTypeEntry { Pattern = @"logged in", Label = "LOGIN", Color = "#a8b4cc", Bg = "rgba(168,180,204,0.08)" },
new MessageTypeEntry { Pattern = @"logged out|inactivity", Label = "LOGOUT", Color = "#556070", Bg = "rgba(85,96,112,0.10)" },
new MessageTypeEntry { Pattern = @"log file cleared", Label = "LOG CLR", Color = "#556070", Bg = "rgba(85,96,112,0.08)" },
};

public static List<QuickFilterEntry> DefaultQuickFilters() =>
[
new() { Label = "Link", Query = "link" },
new() { Label = "NTP", Query = "ntp" },
new() { Label = "Admin", Query = "admin" },
new() { Label = "Config", Query = "configuration" },
];
new List<QuickFilterEntry>
{
new QuickFilterEntry { Label = "Link", Query = "link" },
new QuickFilterEntry { Label = "NTP", Query = "ntp" },
new QuickFilterEntry { Label = "Admin", Query = "admin" },
new QuickFilterEntry { Label = "Config", Query = "configuration" },
};
}

public class SwitchNameEntry
Expand Down
2 changes: 1 addition & 1 deletion csharp/Models/FileGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class FileGroup
{
public string Date { get; set; } = "";
public string? EventsFile { get; set; }
public ObservableCollection<FileItem> Switches { get; set; } = [];
public ObservableCollection<FileItem> Switches { get; set; } = new ObservableCollection<FileItem>();

// Display name for the group header node
public string Header => EventsFile ?? Date;
Expand Down
2 changes: 1 addition & 1 deletion csharp/ScalanceLogs.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
Expand Down
8 changes: 4 additions & 4 deletions csharp/Services/LogManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static class LogManager

private static string _logDir = "";
// (key, writer, lastUsedTicks) — small enough that O(N) eviction is fine
private static readonly Dictionary<string, (StreamWriter writer, long lastUsed)> Writers = [];
private static readonly Dictionary<string, (StreamWriter writer, long lastUsed)> Writers = new Dictionary<string, (StreamWriter writer, long lastUsed)>();
private static readonly object Lock = new();
private static string _currentDate = "";
private static Timer? _cleanupTimer;
Expand Down Expand Up @@ -104,13 +104,13 @@ public static string[] GetLogFiles()
.OrderBy(f => f)
.ToArray()!;
}
catch { return []; }
catch { return Array.Empty<string>(); }
}

public static string[] ReadLines(string filename, int count, string search)
{
var safeName = Path.GetFileName(filename);
if (string.IsNullOrEmpty(safeName)) return [];
if (string.IsNullOrEmpty(safeName)) return Array.Empty<string>();
var path = Path.Combine(_logDir, safeName);
try
{
Expand All @@ -128,7 +128,7 @@ public static string[] ReadLines(string filename, int count, string search)
all = all.Where(l => l.Contains(search, StringComparison.OrdinalIgnoreCase));
return all.TakeLast(count).ToArray();
}
catch { return []; }
catch { return Array.Empty<string>(); }
}

private static string Today() => DateTime.Today.ToString("yyyy-MM-dd");
Expand Down
2 changes: 1 addition & 1 deletion csharp/Services/SyslogParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static class SyslogParser
private static readonly Regex SwitchTagRe = new(@"\(([^)]+)\)", RegexOptions.Compiled);

private static readonly string[] SeverityLabels =
["EMERG", "ALERT", "CRIT", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"];
new string[] { "EMERG", "ALERT", "CRIT", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG" };

// ── Parse raw syslog datagram ────────────────────────────────
public static (int severity, string hostname, string message)
Expand Down
2 changes: 1 addition & 1 deletion csharp/Services/ThemeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace ScalanceLogs.Services;

public static class ThemeManager
{
public static readonly string[] Available = ["Cyber", "Light", "Midnight", "SlateGlass"];
public static readonly string[] Available = new string[] { "Cyber", "Light", "Midnight", "SlateGlass" };

// ── Default palettes ────────────────────────────────────────────
public static readonly Dictionary<string, Dictionary<string, string>> Defaults = new()
Expand Down
26 changes: 13 additions & 13 deletions csharp/SettingsWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ public class BalloonLabelItem
public partial class SettingsWindow : Window
{
private static readonly string[] StandardLabels =
["EMERG", "ALERT", "CRIT", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"];
new string[] { "EMERG", "ALERT", "CRIT", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG" };

private readonly ObservableCollection<SwitchNameEntry> _switches = [];
private readonly ObservableCollection<MessageTypeEntry> _msgTypes = [];
private readonly ObservableCollection<QuickFilterEntry> _quickFilters = [];
private readonly ObservableCollection<string> _patterns = [];
private readonly ObservableCollection<BalloonLabelItem> _balloonItems = [];
private readonly ObservableCollection<ThemeCardItem> _themeCards = [];
private readonly ObservableCollection<SwitchNameEntry> _switches = new ObservableCollection<SwitchNameEntry>();
private readonly ObservableCollection<MessageTypeEntry> _msgTypes = new ObservableCollection<MessageTypeEntry>();
private readonly ObservableCollection<QuickFilterEntry> _quickFilters = new ObservableCollection<QuickFilterEntry>();
private readonly ObservableCollection<string> _patterns = new ObservableCollection<string>();
private readonly ObservableCollection<BalloonLabelItem> _balloonItems = new ObservableCollection<BalloonLabelItem>();
private readonly ObservableCollection<ThemeCardItem> _themeCards = new ObservableCollection<ThemeCardItem>();

public SettingsWindow()
{
Expand Down Expand Up @@ -197,13 +197,13 @@ private bool ApplySettings()
s.AutoStart = AutoStartCheck.IsChecked == true;
s.BalloonNotifications = BalloonCheck.IsChecked == true;
s.BalloonLabels = BalloonAllCheck.IsChecked == true
? []
: [.. _balloonItems.Where(b => b.IsChecked).Select(b => b.Label)];
? new List<string>()
: _balloonItems.Where(b => b.IsChecked).Select(b => b.Label).ToList();

s.SwitchNames = [.. _switches];
s.EventPatterns = [.. _patterns];
s.MessageTypes = [.. _msgTypes];
s.QuickFilters = [.. _quickFilters];
s.SwitchNames = _switches.ToList();
s.EventPatterns = _patterns.ToList();
s.MessageTypes = _msgTypes.ToList();
s.QuickFilters = _quickFilters.ToList();

SettingsService.Save(s);
AutostartHelper.Set(s.AutoStart);
Expand Down
19 changes: 10 additions & 9 deletions csharp/ThemeEditorWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,19 @@ private void OnPC([CallerMemberName] string? n = null) =>
public partial class ThemeEditorWindow : Window
{
private readonly string _themeName;
private readonly ObservableCollection<object> _rows = [];
private readonly ObservableCollection<object> _rows = new ObservableCollection<object>();

// Groups and their keys (TransBrush is always transparent — skip)
private static readonly (string Group, string[] Keys)[] Layout =
[
("BACKGROUNDS", ["BgBrush", "SurfaceBrush", "SubtleBrush"]),
("ACCENT", ["AccentBrush", "Accent2Brush"]),
("TEXT", ["TextBrush", "TextDimBrush", "MutedBrush"]),
("STATUS", ["GreenBrush", "RedBrush", "YellowBrush"]),
("BORDERS & HOVER", ["BorderBrush", "HoverBg", "SelectBg", "PressedBg"]),
("LOG ROW BACKGROUNDS", ["RowErrorBg", "RowWarnBg"]),
];
new (string Group, string[] Keys)[]
{
("BACKGROUNDS", new string[] { "BgBrush", "SurfaceBrush", "SubtleBrush" }),
("ACCENT", new string[] { "AccentBrush", "Accent2Brush" }),
("TEXT", new string[] { "TextBrush", "TextDimBrush", "MutedBrush" }),
("STATUS", new string[] { "GreenBrush", "RedBrush", "YellowBrush" }),
("BORDERS & HOVER", new string[] { "BorderBrush", "HoverBg", "SelectBg", "PressedBg" }),
("LOG ROW BACKGROUNDS", new string[] { "RowErrorBg", "RowWarnBg" }),
};

public ThemeEditorWindow(string themeName)
{
Expand Down
Loading