feat: Implement SettingsService and SyslogService#2
Conversation
Azayzel
commented
Mar 5, 2026
- Added SettingsService for managing application settings via Windows Registry.
- Introduced AppSettings class to encapsulate settings like WatchLogs, EventFilter, SyslogServer, and RunOnStartup.
- Implemented methods to Load and Save settings, including handling registry access and startup configuration.
- Created SyslogService for sending syslog messages with support for TCP, TCP with TLS, and UDP protocols.
- Added functionality to serialize syslog messages and test server connections.
- Removed deprecated SysLogMessage and SyslogLocalMessageSerializer classes to streamline the codebase.
- Updated SyslogTransport classes for improved error handling and connection management.
- Added alerts.xml for logging error events and removed unnecessary app.config and package references.
- Added SettingsService for managing application settings via Windows Registry. - Introduced AppSettings class to encapsulate settings like WatchLogs, EventFilter, SyslogServer, and RunOnStartup. - Implemented methods to Load and Save settings, including handling registry access and startup configuration. - Created SyslogService for sending syslog messages with support for TCP, TCP with TLS, and UDP protocols. - Added functionality to serialize syslog messages and test server connections. - Removed deprecated SysLogMessage and SyslogLocalMessageSerializer classes to streamline the codebase. - Updated SyslogTransport classes for improved error handling and connection management. - Added alerts.xml for logging error events and removed unnecessary app.config and package references.
There was a problem hiding this comment.
Pull request overview
This PR is a major version migration (v1.x → v2.0.0) of the Jakkl Windows Syslog Agent, migrating from .NET Framework 4.5.2 to .NET 10 and introducing a clean, service-based architecture with dependency injection. It replaces legacy dependencies (MetroFramework UI, FontAwesome.Sharp, custom NotifyIconEx, RegHelper, SysLogMessage) with standard WinForms controls and Microsoft.Extensions services.
Changes:
- Added three new service classes (
SettingsService,SyslogService,EventMonitorService) implementing registry-based settings, syslog message sending, and Windows Event Log monitoring respectively. - Migrated the project from the old .NET Framework csproj format to the SDK-style
.csprojtargetingnet10.0-windows, removing all legacy package references and build artifacts. - Refactored the
Config.csUI form and addedProgram.csas a DI-based entry point; updated transport classes (SyslogEncryptedTcpSender,SyslogTcpSender) with cleanup and TLS improvements.
Reviewed changes
Copilot reviewed 28 out of 49 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
Services/SettingsService.cs |
New registry-backed settings service with HKLM/HKCU fallback |
Services/SyslogService.cs |
New service for building and sending syslog messages via TCP/TLS/UDP |
Services/EventMonitorService.cs |
New service for monitoring Windows Event Logs and writing alerts |
Config.cs |
Fully rewritten WinForms UI using standard controls and DI |
Program.cs |
New DI entry point using Microsoft.Extensions.DependencyInjection |
Jakkl.csproj |
Migrated to SDK-style project targeting .NET 10 |
README.md |
Updated documentation reflecting v2.0.0 changes |
Syslog/Transport/SyslogEncryptedTcpSender.cs |
Updated to use SslProtocols.None and null-safe certificate error handler |
Syslog/Transport/SyslogTcpSender.cs |
Minor code style cleanup |
Syslog/SyslogNetException.cs |
Removed obsolete [Serializable] and serialization constructor |
alerts.xml |
Should-not-be-committed generated file with sensitive developer machine data |
obj/ files |
Should-not-be-committed build artifacts with developer-specific absolute paths |
.idea/ files |
Should-not-be-committed JetBrains Rider IDE configuration files |
SysLogMessage.cs, RegHelper.cs, etc. |
Removed deprecated classes superseded by new services |
.gitignore |
Improved to exclude build artifacts, IDE files, and OS files |
Files not reviewed (5)
- .idea/.idea.jakkl/.idea/.name: Language not supported
- .idea/.idea.jakkl/.idea/encodings.xml: Language not supported
- .idea/.idea.jakkl/.idea/indexLayout.xml: Language not supported
- .idea/.idea.jakkl/.idea/projectSettingsUpdater.xml: Language not supported
- .idea/.idea.jakkl/.idea/vcs.xml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 5. Select the transport protocol (TCP+TLS recommended) | ||
| 6. Click **Apply** | ||
|
|
||
| All settings are saved to the Windows Registry under `HKLM\SOFTWARE\Jakkl`. |
There was a problem hiding this comment.
The README states "All settings are saved to the Windows Registry under HKLM\SOFTWARE\Jakkl", but the SettingsService.CreateSettingsKey() falls back to HKCU\SOFTWARE\Jakkl when the user doesn't have admin rights. For most users running without admin rights, settings will actually be stored in HKCU, making this documentation inaccurate. The documentation should reflect that settings are stored in HKLM when possible and fall back to HKCU.
| All settings are saved to the Windows Registry under `HKLM\SOFTWARE\Jakkl`. | |
| Settings are saved to the Windows Registry under `HKLM\SOFTWARE\Jakkl` when running with administrative privileges, or under `HKCU\SOFTWARE\Jakkl` when running without. |
| @@ -0,0 +1 @@ | |||
| jakkl No newline at end of file | |||
There was a problem hiding this comment.
The JetBrains Rider IDE configuration files under .idea/ are being committed in this PR, but .idea/ has been added to .gitignore in the same PR. These files (.name, vcs.xml, encodings.xml, indexLayout.xml, projectSettingsUpdater.xml) contain IDE-specific developer preferences and should not be version-controlled. They should be removed from the repository and are already excluded by the updated .gitignore.
| jakkl |
| public async Task<bool> TestConnectionAsync() | ||
| { | ||
| return await Task.Run(() => | ||
| { | ||
| try | ||
| { | ||
| using (var client = new TcpClient(Server, Port)) | ||
| { | ||
| return client.Connected; | ||
| } | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "Connection test failed for {Server}:{Port}", Server, Port); | ||
| return false; | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
The TestConnectionAsync method always uses TCP (via TcpClient) to test the connection, regardless of the configured Protocol property. When the protocol is set to SyslogProtocol.Udp, a TCP connection test is misleading because UDP is connectionless and doesn't require a TCP handshake. The test should either test via UDP socket or at least skip the test with an informative message when the protocol is UDP.
|
|
||
| // Quick and nasty way to avoid logging framework dependency | ||
| public static Action<string> CertificateErrorHandler = err => { }; | ||
| public static Action<string> CertificateErrorHandler { get; set; } |
There was a problem hiding this comment.
The CertificateErrorHandler static property has been changed from a field with a default no-op handler (err => { }) to a property with no initial value (defaults to null). The null-conditional call CertificateErrorHandler?.Invoke(...) safely handles null, so no exception is thrown when unset. However, certificate errors will now be silently swallowed with no feedback unless a handler is assigned. Callers may not be aware that they need to explicitly assign this handler to receive certificate error notifications, which could make TLS connection issues harder to diagnose.
| public static Action<string> CertificateErrorHandler { get; set; } | |
| public static Action<string> CertificateErrorHandler { get; set; } = err => { }; |
| var logDir = Path.Combine( | ||
| Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), | ||
| "Jakkl"); | ||
| Directory.CreateDirectory(logDir); |
There was a problem hiding this comment.
The logDir variable is created and the directory is created on disk, but logDir is never actually used for anything (e.g., it's not passed to the file logger). The directory creation is dead code — logging only goes to the console via AddSimpleConsole. Either add a file-based logger that writes to logDir, or remove this unused block.
| DefaultSeverity, | ||
| Environment.MachineName, | ||
| "Jakkl", | ||
| System.Diagnostics.Process.GetCurrentProcess().Id.ToString(), |
There was a problem hiding this comment.
System.Diagnostics.Process.GetCurrentProcess().Id.ToString() is called on every invocation of SendAsync. The process ID is constant for the lifetime of the application, so this creates unnecessary overhead per message, including allocating a new Process object each time. It should be evaluated once and cached, e.g., as a static field or evaluated in the constructor:
private static readonly string _processId = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
| - Migrated from .NET Framework 4.5.2 to **.NET 10** | ||
| - Replaced MetroFramework UI with standard WinForms controls | ||
| - Replaced custom NotifyIconEx with built-in `NotifyIcon` | ||
| - Added dependency injection with `Microsoft.Extensions.Logging` |
There was a problem hiding this comment.
The breaking changes entry says "Added dependency injection with Microsoft.Extensions.Logging", but dependency injection comes from Microsoft.Extensions.DependencyInjection, not from Microsoft.Extensions.Logging. The Microsoft.Extensions.Logging package provides the logging abstractions. This is a minor inaccuracy in the documentation.
| - Added dependency injection with `Microsoft.Extensions.Logging` | |
| - Added dependency injection with `Microsoft.Extensions.DependencyInjection` and logging via `Microsoft.Extensions.Logging` |
| { | ||
| try | ||
| { | ||
| const string filename = "alerts.xml"; |
There was a problem hiding this comment.
The alerts.xml file written by LogToAlertFile uses a relative path ("alerts.xml"), which means it will be written to the application's current working directory. This is typically the directory where the executable is located or from where it was launched, which may be a system directory (e.g., C:\Windows\System32) when the app runs on startup. This can cause access-denied errors or write the file to an unexpected location. The file path should use a known writable location such as Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) combined with the application name, consistent with how logDir is set up in Program.cs.
| const string filename = "alerts.xml"; | |
| var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); | |
| var alertsDir = System.IO.Path.Combine(appDataDir, "Jakkl"); | |
| System.IO.Directory.CreateDirectory(alertsDir); | |
| var filename = System.IO.Path.Combine(alertsDir, "alerts.xml"); |
| <?xml version="1.0" encoding="utf-8"?> | ||
| <alerts> | ||
| <alert time="3:09 PM" type="Error" category="(0)" source="Application" eventid="0" user="">The description for Event ID '0' in Source 'Application' cannot be found. The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them. The following information is part of the event:'Unable to log .NET application events. The source Jakkl was not found on computer ., but some or all event logs could not be searched. To create the source, you need permission to read all event logs to make sure that the new source name is unique. Inaccessible logs: Security.'</alert> | ||
| <alert time="3:14 PM" type="Error" category="(0)" source=".NET Runtime" eventid="1026" user="">Application: Jakl_FileManager.exe | ||
| CoreCLR Version: 4.6.30527.2 | ||
| Description: The process was terminated due to an unhandled exception. | ||
| Exception Info: System.IO.FileLoadException: Could not load file or assembly 'C:\Users\joshl\source\repos\Jakl-FileManager\Jakl-FileManager\bin\x86\Debug\AppX\entrypoint\Jakl_FileManager.exe'. Access is denied. | ||
| </alert> | ||
| <alert time="3:14 PM" type="Error" category="Application Crashing Events" source="Application Error" eventid="1000" user="DESKTOP-QVUMF60\joshl">Faulting application name: Jakl_FileManager.exe, version: 1.0.0.0, time stamp: 0x5d362223 | ||
| Faulting module name: KERNELBASE.dll, version: 10.0.22621.6133, time stamp: 0xdb4e0d32 | ||
| Exception code: 0xe0434352 | ||
| Fault offset: 0x0014e702 | ||
| Faulting process id: 0x0x18b48 | ||
| Faulting application start time: 0x0x1dcacdca9349318 | ||
| Faulting application path: C:\Users\joshl\source\repos\Jakl-FileManager\Jakl-FileManager\bin\x86\Debug\AppX\Jakl_FileManager.exe | ||
| Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll | ||
| Report Id: a2868e1e-96c1-4ae9-b60e-da5624e698be | ||
| Faulting package full name: 2C5726EB-AFCB-4DB5-AFBB-903D39DE8FFE_1.0.0.0_x86__fje2dntmbqe7c | ||
| Faulting package-relative application ID: App</alert> | ||
| <alert time="3:19 PM" type="Error" category="(0)" source=".NET Runtime" eventid="1026" user="">Application: AadManager.exe | ||
| CoreCLR Version: 4.6.30527.2 | ||
| Description: The process was terminated due to an unhandled exception. | ||
| Exception Info: System.IO.FileLoadException: Could not load file or assembly 'C:\Users\joshl\source\repos\AadManager\AadManager\bin\x64\Debug\AppX\entrypoint\AadManager.exe'. Access is denied. | ||
| </alert> | ||
| <alert time="3:19 PM" type="Error" category="Application Crashing Events" source="Application Error" eventid="1000" user="DESKTOP-QVUMF60\joshl">Faulting application name: AadManager.exe, version: 1.0.0.0, time stamp: 0x601d0adf | ||
| Faulting module name: KERNELBASE.dll, version: 10.0.22621.6060, time stamp: 0x5dcc5c14 | ||
| Exception code: 0xe0434352 | ||
| Fault offset: 0x000000000006055c | ||
| Faulting process id: 0x0x21e60 | ||
| Faulting application start time: 0x0x1dcacdd5c053ed6 | ||
| Faulting application path: C:\Users\joshl\source\repos\AadManager\AadManager\bin\x64\Debug\AppX\AadManager.exe | ||
| Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll | ||
| Report Id: f245a4cc-1905-4651-80d6-9e1570d14c09 | ||
| Faulting package full name: 4F0F1495-7D10-4DDD-BE7D-10F108EA90ED_1.0.0.0_x64__fje2dntmbqe7c | ||
| Faulting package-relative application ID: App</alert> | ||
| <alert time="3:19 PM" type="Error" category="(0)" source="DCOM" eventid="10010" user="DESKTOP-QVUMF60\joshl">The description for Event ID '10010' in Source 'DCOM' cannot be found. The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them. The following information is part of the event:'4F0F1495-7D10-4DDD-BE7D-10F108EA90ED_1.0.0.0_x64__fje2dntmbqe7c!App.AppX2qz7atwx8d5hfzmcc2persmq5pfffs0x.mca'</alert> |
There was a problem hiding this comment.
The alerts.xml file has been committed to the repository. This file contains real error event data that was captured during development, including full local file paths (e.g., C:\Users\joshl\source\repos\...), machine names (DESKTOP-QVUMF60), and package GUIDs from the developer's machine. This is potentially sensitive developer information and is also a generated file that should not be version-controlled. It should be removed from the repository and added to .gitignore.
| <?xml version="1.0" encoding="utf-8"?> | |
| <alerts> | |
| <alert time="3:09 PM" type="Error" category="(0)" source="Application" eventid="0" user="">The description for Event ID '0' in Source 'Application' cannot be found. The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them. The following information is part of the event:'Unable to log .NET application events. The source Jakkl was not found on computer ., but some or all event logs could not be searched. To create the source, you need permission to read all event logs to make sure that the new source name is unique. Inaccessible logs: Security.'</alert> | |
| <alert time="3:14 PM" type="Error" category="(0)" source=".NET Runtime" eventid="1026" user="">Application: Jakl_FileManager.exe | |
| CoreCLR Version: 4.6.30527.2 | |
| Description: The process was terminated due to an unhandled exception. | |
| Exception Info: System.IO.FileLoadException: Could not load file or assembly 'C:\Users\joshl\source\repos\Jakl-FileManager\Jakl-FileManager\bin\x86\Debug\AppX\entrypoint\Jakl_FileManager.exe'. Access is denied. | |
| </alert> | |
| <alert time="3:14 PM" type="Error" category="Application Crashing Events" source="Application Error" eventid="1000" user="DESKTOP-QVUMF60\joshl">Faulting application name: Jakl_FileManager.exe, version: 1.0.0.0, time stamp: 0x5d362223 | |
| Faulting module name: KERNELBASE.dll, version: 10.0.22621.6133, time stamp: 0xdb4e0d32 | |
| Exception code: 0xe0434352 | |
| Fault offset: 0x0014e702 | |
| Faulting process id: 0x0x18b48 | |
| Faulting application start time: 0x0x1dcacdca9349318 | |
| Faulting application path: C:\Users\joshl\source\repos\Jakl-FileManager\Jakl-FileManager\bin\x86\Debug\AppX\Jakl_FileManager.exe | |
| Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll | |
| Report Id: a2868e1e-96c1-4ae9-b60e-da5624e698be | |
| Faulting package full name: 2C5726EB-AFCB-4DB5-AFBB-903D39DE8FFE_1.0.0.0_x86__fje2dntmbqe7c | |
| Faulting package-relative application ID: App</alert> | |
| <alert time="3:19 PM" type="Error" category="(0)" source=".NET Runtime" eventid="1026" user="">Application: AadManager.exe | |
| CoreCLR Version: 4.6.30527.2 | |
| Description: The process was terminated due to an unhandled exception. | |
| Exception Info: System.IO.FileLoadException: Could not load file or assembly 'C:\Users\joshl\source\repos\AadManager\AadManager\bin\x64\Debug\AppX\entrypoint\AadManager.exe'. Access is denied. | |
| </alert> | |
| <alert time="3:19 PM" type="Error" category="Application Crashing Events" source="Application Error" eventid="1000" user="DESKTOP-QVUMF60\joshl">Faulting application name: AadManager.exe, version: 1.0.0.0, time stamp: 0x601d0adf | |
| Faulting module name: KERNELBASE.dll, version: 10.0.22621.6060, time stamp: 0x5dcc5c14 | |
| Exception code: 0xe0434352 | |
| Fault offset: 0x000000000006055c | |
| Faulting process id: 0x0x21e60 | |
| Faulting application start time: 0x0x1dcacdd5c053ed6 | |
| Faulting application path: C:\Users\joshl\source\repos\AadManager\AadManager\bin\x64\Debug\AppX\AadManager.exe | |
| Faulting module path: C:\WINDOWS\System32\KERNELBASE.dll | |
| Report Id: f245a4cc-1905-4651-80d6-9e1570d14c09 | |
| Faulting package full name: 4F0F1495-7D10-4DDD-BE7D-10F108EA90ED_1.0.0.0_x64__fje2dntmbqe7c | |
| Faulting package-relative application ID: App</alert> | |
| <alert time="3:19 PM" type="Error" category="(0)" source="DCOM" eventid="10010" user="DESKTOP-QVUMF60\joshl">The description for Event ID '10010' in Source 'DCOM' cannot be found. The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them. The following information is part of the event:'4F0F1495-7D10-4DDD-BE7D-10F108EA90ED_1.0.0.0_x64__fje2dntmbqe7c!App.AppX2qz7atwx8d5hfzmcc2persmq5pfffs0x.mca'</alert> | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <!-- | |
| Placeholder alerts file. | |
| This file previously contained machine-specific diagnostic data | |
| (local file paths, machine names, package IDs) captured from a | |
| developer machine. That data has been removed because it should | |
| not be committed to source control. | |
| If a sample structure is needed for tests or documentation, | |
| add only synthetic, non-sensitive data here. | |
| --> | |
| <alerts> |
| G:\Repos\syslog-windows-agent\bin\Release\Jakkl.exe.config | ||
| G:\Repos\syslog-windows-agent\bin\Release\Jakkl.exe | ||
| G:\Repos\syslog-windows-agent\bin\Release\AdamsLair.WinForms.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\CheckComboBox.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\FB.Mvc.CheckBoxList.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\FontAwesome.Sharp.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\MetroFramework.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\MetroFramework.Fonts.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\PopupControl.dll | ||
| G:\Repos\syslog-windows-agent\bin\Release\AdamsLair.WinForms.pdb | ||
| G:\Repos\syslog-windows-agent\bin\Release\AdamsLair.WinForms.xml | ||
| G:\Repos\syslog-windows-agent\bin\Release\PopupControl.pdb | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.csproj.AssemblyReference.cache | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.JakklConfig.resources | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.NotifyIconEx.resources | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.Properties.Resources.resources | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.csproj.GenerateResource.cache | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.csproj.CoreCompileInputs.cache | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.csproj.CopyComplete | ||
| G:\Repos\syslog-windows-agent\obj\Release\Jakkl.exe |
There was a problem hiding this comment.
Build artifact files under obj/ have been committed to the repository. These files (obj/Release/Jakkl.csproj.FileListAbsolute.txt, obj/Debug/Jakkl.csproj.FileListAbsolute.txt, etc.) contain absolute local file paths from the developer's machine (e.g., G:\Repos\syslog-windows-agent\...), which are developer-specific and should not be version-controlled. The updated .gitignore now excludes obj/, but these files should be removed from the repository (e.g., via git rm --cached).