XmlLoggingConfiguration - AutoReload available by default as before#5782
XmlLoggingConfiguration - AutoReload available by default as before#5782
Conversation
|
Warning Rate limit exceeded@snakefoot has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 27 minutes and 45 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (12)
WalkthroughThis change removes the Changes
Sequence Diagram(s)sequenceDiagram
participant Application
participant LogFactory
participant XmlLoggingConfiguration
participant AutoReloadConfigFileWatcher
participant FileSystem
Application->>LogFactory: Assign XmlLoggingConfiguration
LogFactory->>XmlLoggingConfiguration: OnConfigurationAssigned()
XmlLoggingConfiguration->>AutoReloadConfigFileWatcher: Create or refresh watcher
AutoReloadConfigFileWatcher->>FileSystem: Start watching config files
FileSystem-->>AutoReloadConfigFileWatcher: File change event
AutoReloadConfigFileWatcher->>AutoReloadConfigFileWatcher: Debounce & schedule reload
AutoReloadConfigFileWatcher->>XmlLoggingConfiguration: Reload configuration
XmlLoggingConfiguration->>LogFactory: Activate new configuration (if changed)
Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/NLog/Config/XmlLoggingConfiguration.cs (1)
564-737: Consider replacing the static dictionary withConditionalWeakTableEven with the leak fix above, a static cache keyed by
LogFactorycan grow unbounded in applications that create many transient factories (e.g., in unit tests).
ConditionalWeakTable<LogFactory, AutoReloadConfigFileWatcher>would automatically release entries when the factory becomes unreachable, without explicit bookkeeping.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj(0 hunks)src/NLog.AutoReloadConfig/README.md(0 hunks)src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs(0 hunks)src/NLog.sln(0 hunks)src/NLog/Config/XmlLoggingConfiguration.cs(5 hunks)tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj(0 hunks)tests/NLog.UnitTests/Config/AutoReloadTests.cs(1 hunks)
💤 Files with no reviewable changes (5)
- src/NLog.sln
- src/NLog.AutoReloadConfig/README.md
- tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj
- src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj
- src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs
🔇 Additional comments (1)
tests/NLog.UnitTests/Config/AutoReloadTests.cs (1)
35-35: Namespace change looks goodThe rename to
NLog.UnitTests.Configaligns the file with the rest of the unit–test project structure introduced by the merge. No further action needed.
2c9d478 to
c62ea33
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
src/NLog/Config/XmlLoggingConfiguration.cs (4)
570-570:⚠️ Potential issueThread-safety: make
_isDisposingvolatile
_isDisposingis read from multiple threads (timer thread, FileSystemWatcher thread) without a lock. Without thevolatilemodifier there is no guarantee other threads will observe the write performed inDispose(), leading to possible use-after-dispose race conditions.- private bool _isDisposing; + private volatile bool _isDisposing;
576-578:⚠️ Potential issueDead-code / missing subscription for
LogFactory.ConfigurationChangedThe
AutoReloadConfigFileWatcherclass implementsLogFactory_ConfigurationChanged, but the constructor never subscribes to the event, andDispose()never unsubscribes.Either wire up the event or remove the unused method:
public AutoReloadConfigFileWatcher(LogFactory logFactory) { _logFactory = logFactory; _fileWatcher.FileChanged += FileWatcher_FileChanged; + _logFactory.ConfigurationChanged += LogFactory_ConfigurationChanged; }And in the Dispose method:
public void Dispose() { _isDisposing = true; _fileWatcher.FileChanged -= FileWatcher_FileChanged; + _logFactory.ConfigurationChanged -= LogFactory_ConfigurationChanged; lock (_lockObject) { var reloadTimer = _reloadTimer; _reloadTimer = null; reloadTimer?.Dispose(); } _fileWatcher.Dispose(); }
56-56:⚠️ Potential issuePotential memory leak in the static
_watcherscacheThe
_watchersdictionary maintains strong references to eachLogFactoryinstance. When a factory is disposed, its watcher is disposed too, but the dictionary entry is never removed. This could prevent both theLogFactoryand theAutoReloadConfigFileWatcherinstances from being garbage-collected.Add cleanup code to remove entries when no longer needed:
- private static readonly Dictionary<LogFactory, AutoReloadConfigFileWatcher> _watchers = new Dictionary<LogFactory, AutoReloadConfigFileWatcher>(); + private static readonly Dictionary<LogFactory, AutoReloadConfigFileWatcher> _watchers = new Dictionary<LogFactory, AutoReloadConfigFileWatcher>(WeakReferenceComparer.Default);Or add code to remove entries when the watcher is disposed:
private void OnConfigurationAssigned(LogFactory logFactory) { // ... if (logFactory is null || !AutoReload) { if (fileWatcher != null) { InternalLogger.Info("AutoReload Config File Monitor stopping, since no active configuration"); fileWatcher.Dispose(); + lock (_watchers) + { + _watchers.Remove(configFactory); + } } } // ... }
220-221:⚠️ Potential issueMissing dictionary cleanup when disposing watchers
When the auto-reload feature is disabled or the LogFactory is null, the watcher is disposed but the entry remains in the dictionary. This could lead to a memory leak over time.
if (fileWatcher != null) { InternalLogger.Info("AutoReload Config File Monitor stopping, since no active configuration"); fileWatcher.Dispose(); + lock (_watchers) + { + _watchers.Remove(configFactory); + } }
🧹 Nitpick comments (3)
src/NLog/Config/XmlLoggingConfiguration.cs (3)
610-631: Consider using CancellationTokenSource for better timer managementThe current approach of creating and disposing Timer objects could be simplified using a CancellationTokenSource with a delay. This would make the code more maintainable and reduce the chance of resource leaks.
- private Timer _reloadTimer; + private CancellationTokenSource _reloadCts; private void FileWatcher_FileChanged(object sender, System.IO.FileSystemEventArgs e) { lock (_lockObject) { if (_isDisposing) return; - var reloadTimer = _reloadTimer; - if (reloadTimer is null) + var currentCts = _reloadCts; + if (currentCts != null) { - var currentConfig = _logFactory.Configuration; - if (currentConfig is null) - return; - - _reloadTimer = new Timer((s) => ReloadTimer(s), currentConfig, 1000, Timeout.Infinite); + currentCts.Cancel(); } - else + + var currentConfig = _logFactory.Configuration; + if (currentConfig is null) + return; + + _reloadCts = new CancellationTokenSource(); + var token = _reloadCts.Token; + Task.Delay(1000, token).ContinueWith(t => { - reloadTimer.Change(1000, Timeout.Infinite); + if (!t.IsCanceled) + ReloadTimer(currentConfig); + }, TaskContinuationOptions.NotOnCanceled); } }
633-639: Use early return pattern consistentlyThe early return pattern is used inconsistently. For better readability, consider applying it more uniformly.
private void ReloadTimer(object state) { if (_isDisposing) - { - return; //timer was disposed already. - } + return; //timer was disposed already. LoggingConfiguration oldConfig = (LoggingConfiguration)state;
716-718: Release all resources in Dispose methodEnsure all resources are properly released in the Dispose method, including any event subscriptions that might have been set up.
public void Dispose() { _isDisposing = true; _fileWatcher.FileChanged -= FileWatcher_FileChanged; + // If you choose to implement ConfigurationChanged event subscription + // _logFactory.ConfigurationChanged -= LogFactory_ConfigurationChanged; lock (_lockObject) { var reloadTimer = _reloadTimer; _reloadTimer = null; reloadTimer?.Dispose(); } _fileWatcher.Dispose(); + TryUnwatchConfigFile(); // Ensure file watching is stopped }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj(0 hunks)src/NLog.AutoReloadConfig/README.md(0 hunks)src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs(0 hunks)src/NLog.sln(0 hunks)src/NLog/Config/XmlLoggingConfiguration.cs(5 hunks)tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj(0 hunks)tests/NLog.UnitTests/Config/AutoReloadTests.cs(9 hunks)
💤 Files with no reviewable changes (5)
- src/NLog.sln
- src/NLog.AutoReloadConfig/README.md
- tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj
- src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs
- src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/NLog.UnitTests/Config/AutoReloadTests.cs
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
src/NLog/Config/XmlLoggingConfiguration.cs (3)
575-575:⚠️ Potential issueThread-safety: make
_isDisposingvolatile
_isDisposingis read from multiple threads (timer thread, FileSystemWatcher thread) without a lock. Without thevolatilemodifier there is no guarantee other threads will observe the write performed inDispose(), leading to possible use-after-dispose race conditions.- private bool _isDisposing; + private volatile bool _isDisposing;
56-57:⚠️ Potential issueFix potential memory leak in static
_watchersdictionaryThe
_watchersdictionary maintains strong references toLogFactoryinstances, but entries are never removed when factories are disposed or auto-reload is disabled.Update the
OnConfigurationAssignedmethod to remove disposed entries:if (fileWatcher != null) { InternalLogger.Info("AutoReload Config File Monitor stopping, since no active configuration"); fileWatcher.Dispose(); + lock (_watchers) + { + _watchers.Remove(configFactory); + } }
579-583: 🛠️ Refactor suggestionMissing event subscription for
LogFactory_ConfigurationChangedThe class implements a method for handling
LogFactory.ConfigurationChangedevents, but never subscribes to this event in the constructor or unsubscribes inDispose().Either wire up the event handler or remove the unused method to avoid confusion:
public AutoReloadConfigFileWatcher(LogFactory logFactory) { _logFactory = logFactory; _fileWatcher.FileChanged += FileWatcher_FileChanged; + _logFactory.ConfigurationChanged += LogFactory_ConfigurationChanged; } // And in Dispose(): public void Dispose() { _isDisposing = true; _fileWatcher.FileChanged -= FileWatcher_FileChanged; + _logFactory.ConfigurationChanged -= LogFactory_ConfigurationChanged; lock (_lockObject) { var reloadTimer = _reloadTimer; _reloadTimer = null; reloadTimer?.Dispose(); } _fileWatcher.Dispose(); }
🧹 Nitpick comments (1)
tests/NLog.UnitTests/Config/ReloadTests.cs (1)
530-533: Consider migrating remaining uses of obsolete propertyWhile pragma directives correctly suppress the warning, for consistency and future maintenance it would be better to migrate these remaining usages to
AutoReloadFileNamesas well.-#pragma warning disable CS0618 // Type or member is obsolete - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); -#pragma warning restore CS0618 // Type or member is obsolete + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault());
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
build.ps1(0 hunks)run-tests.ps1(0 hunks)src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj(0 hunks)src/NLog.AutoReloadConfig/README.md(0 hunks)src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs(0 hunks)src/NLog.sln(0 hunks)src/NLog/Config/LoggingConfiguration.cs(2 hunks)src/NLog/Config/XmlLoggingConfiguration.cs(6 hunks)tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj(0 hunks)tests/NLog.UnitTests/Config/AutoReloadTests.cs(9 hunks)tests/NLog.UnitTests/Config/ReloadTests.cs(12 hunks)tests/NLog.UnitTests/ConfigFileLocatorTests.cs(1 hunks)
💤 Files with no reviewable changes (7)
- build.ps1
- run-tests.ps1
- src/NLog.AutoReloadConfig/README.md
- src/NLog.sln
- tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj
- src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj
- src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs
✅ Files skipped from review due to trivial changes (3)
- tests/NLog.UnitTests/Config/AutoReloadTests.cs
- tests/NLog.UnitTests/ConfigFileLocatorTests.cs
- src/NLog/Config/LoggingConfiguration.cs
🔇 Additional comments (4)
tests/NLog.UnitTests/Config/ReloadTests.cs (1)
98-99: Code successfully updated to use newAutoReloadFileNamespropertyAll test assertions have been correctly updated to reference the new
AutoReloadFileNamesproperty instead of the obsoleteFileNamesToWatchproperty. This maintains test coverage while adhering to the new API.Also applies to: 107-108, 116-117, 161-162, 170-171, 181-182, 299-299, 307-307, 318-318, 365-366, 374-375, 432-434, 442-444, 452-454, 511-513, 521-522
src/NLog/Config/XmlLoggingConfiguration.cs (3)
183-186: Appropriate use of obsolete attributeGood implementation of backward compatibility by marking the
FileNamesToWatchproperty as obsolete and delegating to the newAutoReloadFileNamesproperty.
206-249: Well-implemented configuration lifecycle managementThe
OnConfigurationAssignedmethod correctly handles the lifecycle of file watchers based on configuration changes. The use of locking for thread safety and proper error handling is commendable.
569-712: Well-implemented auto-reload mechanismThe
AutoReloadConfigFileWatcherclass provides a robust implementation with:
- Appropriate debouncing through timer usage
- Thread-safe coordination of file watching and reloading
- Proper error handling to prevent crashes
- Clean disposal of resources
The integration of this functionality directly into
XmlLoggingConfigurationensures the feature works out of the box.
f04f517 to
a9f9507
Compare
67efed0 to
425f105
Compare
|


Merged NLog.AutoReloadConfig-project from #5649 back into the NLog-project XmlLoggingConfiguration. Now
autoReload="true"will work out of the box just like in previous NLog-versions.There was little difference in AOT-build-size, and the goal is that XmlLoggingConfiguration has to be explicit loaded.
Now marked
LoggingConfiguration.FileNamesToWatchas obsolete, since NLog LogFactory does not have any FileWatcher-capabilities.