Skip to content

Fix/reduce event loss on reconfigurationreduce silent log event loss during XmlConfigurator reconfiguration#287

Open
N0tre3l wants to merge 3 commits intoapache:masterfrom
N0tre3l:fix/reduce-event-loss-on-reconfiguration
Open

Fix/reduce event loss on reconfigurationreduce silent log event loss during XmlConfigurator reconfiguration#287
N0tre3l wants to merge 3 commits intoapache:masterfrom
N0tre3l:fix/reduce-event-loss-on-reconfiguration

Conversation

@N0tre3l
Copy link
Copy Markdown

@N0tre3l N0tre3l commented Apr 8, 2026

Problem

During reconfiguration (XmlConfigurator.Configure in Merge mode),
XmlHierarchyConfigurator.ParseChildrenOfLoggerElement called
RemoveAllAppenders() first, then parsed the XML and re-added appenders
one by one. Any log event fired during the XML parse window (10-50 ms in
typical applications) was silently dropped because CallAppenders found
_appenderAttachedImpl == null.

Measured impact in a reproducible test: 95.9 % of events lost during a
50 ms simulated reconfiguration window.

Changes

Logger.cs - new ReplaceAppenders(IEnumerable<IAppender>) method

Removes the old appender collection and adds all new appenders inside a
single writer lock, so the logger is never in a zero-appender state for
more than a few microseconds (the lock acquisition time).

XmlHierarchyConfigurator.cs - collect-then-swap in ParseChildrenOfLoggerElement

All new appenders are resolved from XML into a List<IAppender> before
the live logger is touched. Then Logger.ReplaceAppenders() is called once
to perform the atomic swap, rather than the previous remove-then-add-one-by-one
pattern.

XmlHierarchyConfigurator.cs - reset EmittedNoAppenderWarning in Configure

The "no appender" warning is intentionally designed to fire once per
application lifetime. However, with the appender swap window now reduced to
microseconds, an unlucky event hitting that window would permanently suppress
the diagnostic for the rest of the application lifetime. Resetting the flag at
the start of each configuration pass restores the intended behavior: one
warning per reconfiguration cycle if events are lost.

The reset is placed in Configure() rather than Hierarchy.ResetConfiguration()
because ResetConfiguration() is only called in Overwrite mode, which would
leave Merge mode - the default and the affected mode - without the reset.

Testing

Manually verified that:

  • XmlConfigurator.Configure still attaches the correct appenders after
    reconfiguration in both Merge and Overwrite modes.
  • The zero-appender window is no longer observable under concurrent logging load.

N0tre3l added 3 commits April 8, 2026 19:57
Add method to atomically replace all appenders with a new collection.
…nderWarning in ResetConfigurationittedNoAppenderWarning

Reset the warning flag to allow diagnostics during reconfiguration.
…ize null window

Previously, ParseChildrenOfLoggerElement called RemoveAllAppenders()
before adding new ones one-by-one, creating a window (equal to the full
XML parse duration, typically 10-50 ms) during which the logger had no
appenders and log events were silently dropped.

This change resolves the race by:
1. Collecting all incoming appenders into a local List<IAppender> first.
2. Calling Logger.ReplaceAppenders() to perform the swap in a single
   writer lock, reducing the zero-appender window to microseconds.
Copy link
Copy Markdown
Contributor

@FreeAndNil FreeAndNil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@N0tre3l thanks for your contribution.
Could you please add some unit tests and fix the compiler errors?

// Reset the warning flag so diagnostics can fire during the next
// reconfiguration cycle. Without this, the "no appender" warning is
// silenced permanently after the first configuration.
EmittedNoAppenderWarning = false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// event loss during reconfiguration.
/// </para>
/// </remarks>
public virtual void ReplaceAppenders(IEnumerable<IAppender> appenders)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please adjust the indentation


// Phase 1: resolve all new appenders from XML *before* touching the
// live logger. This avoids the window where the logger has no appenders.
var newAppenders = new List<IAppender>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var newAppenders = new List<IAppender>();
List<IAppender> newAppenders = new();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants