Skip to content

InternalLogger - Auto enable LogLevel.Info when activated#6070

Merged
snakefoot merged 1 commit intoNLog:devfrom
snakefoot:internallogger_info
Jan 31, 2026
Merged

InternalLogger - Auto enable LogLevel.Info when activated#6070
snakefoot merged 1 commit intoNLog:devfrom
snakefoot:internallogger_info

Conversation

@snakefoot
Copy link
Contributor

@snakefoot snakefoot commented Jan 2, 2026

Partially reverts #4547 and #2935 - Auto-enable LogLevel.Info unless explicitly configured to LogLevel.Off.

Has the side-effect that explicit configuring internalLogLevel="Off" gives a minor performance hit.

Also has the side-effect that if having explicit configured internalLogToConsole="true" or internalLogToConsoleError="true" or internalLogFile="...", then logging is now automatically activated without having assigned internalLogLevel (Similar behavior as NLog v4.7.15 and older)

@coderabbitai
Copy link

coderabbitai bot commented Jan 2, 2026

Walkthrough

InternalLogger now uses a nullable backing for its log level and lazily initializes it (to Info) when writers or console/file flags are enabled; LogWriter gained a backing field. LoggingConfigurationParser no longer auto-disables InternalLogger. Several tests were updated to call InternalLogger.Reset() and minor test cleanups performed.

Changes

Cohort / File(s) Summary
InternalLogger refactor
src/NLog/Common/InternalLogger.cs
Backing _logLevel changed to nullable (LogLevel?); LogLevel getter returns _logLevel ?? LogLevel.Off; LogWriter now has a private backing _logWriter with a setter that sets _logLevel = Info when a non-null writer is assigned and _logLevel is null; enabling console/file flags similarly initialize _logLevel; write path uses _logWriter; HasActiveLoggers() removed; Reset() clears _logLevel to null.
Logging configuration parser
src/NLog/Config/LoggingConfigurationParser.cs
Removed internal internalLoggerEnabled tracking and logic that auto-set InternalLogger.LogLevel to Off when no active loggers were detected.
Config section visibility
src/NLog/Config/ConfigSectionHandler.cs
AppConfig access modifier changed to public (public static LoggingConfiguration? AppConfig), exposing the existing accessor.
Unit tests — XML config
tests/NLog.UnitTests/Config/XmlConfigTests.cs
Added InternalLogger.Reset() at test starts to ensure a cleared internal-logger state before assertions; inline comments added to clarify expected auto-enabled/not-auto-enabled states.
Unit tests — InternalLogger
tests/NLog.UnitTests/Common/InternalLoggerTests.cs
Added test sequence invoking InternalLogger.Reset() and assertions that all Is*Enabled checks are false after reset.
Unit tests — layout renderer cleanup
tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs
Removed unused usings and a private debug target field; assertions now use AssertDebugLastMessage(...) helper instead of per-test target access.
Unit tests — mocks cleanup
tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs
Removed unused using System.Xml directive; no behavioral changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇
I nudged a nullable sprout in the log,
Unset waits patient beneath the loggish fog,
Reset clears the burrow, Info wakes with a nudge,
No extra checkers now scurry and judge,
A hop, a tweak, and the debug carrots sing.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: auto-enabling LogLevel.Info when the internal logger is activated.
Description check ✅ Passed The description clearly explains the change, references related PRs, and documents both the primary behavior and known side-effects.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71b385a and eaf866e.

📒 Files selected for processing (7)
  • src/NLog/Common/InternalLogger.cs
  • src/NLog/Config/ConfigSectionHandler.cs
  • src/NLog/Config/LoggingConfigurationParser.cs
  • tests/NLog.UnitTests/Common/InternalLoggerTests.cs
  • tests/NLog.UnitTests/Config/XmlConfigTests.cs
  • tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs
  • tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs
💤 Files with no reviewable changes (2)
  • src/NLog/Config/LoggingConfigurationParser.cs
  • tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs
  • src/NLog/Config/ConfigSectionHandler.cs
  • tests/NLog.UnitTests/Config/XmlConfigTests.cs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-12T01:22:52.437Z
Learnt from: thatrajeevkr
Repo: NLog/NLog PR: 5999
File: src/NLog/Layouts/JSON/JsonLayout.cs:162-166
Timestamp: 2025-09-12T01:22:52.437Z
Learning: When suggesting StringBuilder optimizations for NLog layouts marked as [ThreadAgnostic], avoid shared StringBuilder fields as they create thread safety issues. Prefer simple string operations or ThreadLocal<StringBuilder> if allocation profiling shows a real performance impact.

Applied to files:

  • src/NLog/Common/InternalLogger.cs
🧬 Code graph analysis (1)
src/NLog/Common/InternalLogger.cs (1)
src/NLog/LogLevel.cs (3)
  • LogLevel (136-140)
  • LogLevel (253-275)
  • LogLevel (282-337)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: task-list-completed
🔇 Additional comments (6)
src/NLog/Common/InternalLogger.cs (5)

62-68: Reset logic correctly initializes nullable backing field.

The Reset method appropriately nulls _logLevel to restore the uninitialized state, allowing subsequent activation of console/file/writer outputs to auto-enable Info level logging per the PR objectives.


74-79: Nullable backing field pattern correctly implemented.

The LogLevel property correctly uses a nullable backing field to distinguish between unconfigured (null, defaults to Off) and explicitly configured states. This enables the auto-enable behavior when outputs are activated.


320-329: Correct usage of backing field in Write method.

The early exit check at line 320 correctly uses the _logWriter backing field instead of the public property, avoiding unnecessary property getter overhead and potential setter side effects. The added clarifying comment at line 327 is helpful.


367-374: WriteToLog correctly uses backing field within synchronized block.

The method appropriately uses _logWriter backing field (lines 367, 372) to avoid re-triggering setter logic, and protects the write operation with the lock. The null-conditional operator at line 372 adds defensive safety.


430-430: IsLogLevelEnabled correctly handles nullable backing field.

The ordinal comparison correctly handles the nullable _logLevel by checking for null first, ensuring the method returns false when logging is unconfigured. The additional null check on the logLevel parameter provides defensive safety.

tests/NLog.UnitTests/Common/InternalLoggerTests.cs (1)

64-72: Test correctly validates new Reset behavior.

The added test sequence appropriately verifies that InternalLogger.Reset() disables internal logging by nulling the backing field, causing all Is*Enabled properties to return false. This provides good coverage for the nullable LogLevel behavior introduced in this PR.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@snakefoot snakefoot force-pushed the internallogger_info branch 9 times, most recently from e044f4d to 5c3796f Compare January 2, 2026 16:22
@snakefoot snakefoot added the breaking behavior change Same API, different result label Jan 2, 2026
@snakefoot snakefoot force-pushed the internallogger_info branch 2 times, most recently from 24b9da3 to 391ad42 Compare January 2, 2026 16:37
@snakefoot snakefoot changed the title InternalLogger - Enable LogLevel.Info by default InternalLogger - Auto enable LogLevel.Info by default Jan 2, 2026
@snakefoot snakefoot changed the title InternalLogger - Auto enable LogLevel.Info by default InternalLogger - Auto enable LogLevel.Info when activated Jan 2, 2026
@snakefoot snakefoot closed this Jan 2, 2026
@snakefoot snakefoot reopened this Jan 2, 2026
@snakefoot snakefoot closed this Jan 2, 2026
@snakefoot snakefoot reopened this Jan 2, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 2, 2026

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/NLog/Common/InternalLogger.cs (1)

85-102: Potential race condition in auto-enable logic.

The check-and-set pattern at lines 96-97 is not atomic. If multiple threads concurrently enable console logging during initialization, there's a race on _logLevel. However, since the InternalLogger is typically configured once at startup and the race would only result in redundant assignments to Info (not corruption), this may be acceptable.

Consider whether explicit locking or Interlocked.CompareExchange is needed based on your expected usage patterns.

♻️ Duplicate comments (2)
src/NLog/Common/InternalLogger.cs (2)

109-126: Same race condition applies here.

Lines 120-121 have the same non-atomic check-and-set pattern as LogToConsole. The same analysis applies: likely acceptable given typical usage, but worth considering for consistency.


133-161: Same race condition applies here.

Lines 148-149 have the same non-atomic check-and-set pattern. Consistent with the other output setters.

🧹 Nitpick comments (1)
src/NLog/Common/InternalLogger.cs (1)

428-431: Consider more idiomatic null check.

The logic at line 430 is correct, but !ReferenceEquals(_logLevel, null) is less idiomatic than _logLevel is not null or _logLevel != null. While ReferenceEquals is functionally equivalent (and avoids operator overloading), modern C# favors pattern matching for readability.

🔎 Suggested refactor
-return !ReferenceEquals(_logLevel, null) && _logLevel.CompareTo(logLevel) <= 0;
+return _logLevel is not null && _logLevel.CompareTo(logLevel) <= 0;
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb99454 and 391ad42.

📒 Files selected for processing (3)
  • src/NLog/Common/InternalLogger.cs
  • src/NLog/Config/LoggingConfigurationParser.cs
  • tests/NLog.UnitTests/Config/XmlConfigTests.cs
💤 Files with no reviewable changes (1)
  • src/NLog/Config/LoggingConfigurationParser.cs
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/Common/InternalLogger.cs (1)
src/NLog/LogLevel.cs (5)
  • LogLevel (136-140)
  • LogLevel (253-275)
  • LogLevel (282-337)
  • CompareTo (389-392)
  • CompareTo (404-407)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (39)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (5)
src/NLog/Common/InternalLogger.cs (3)

58-68: LGTM: Reset correctly initializes to unconfigured state.

The change to set _logLevel = null (instead of LogLevel.Off) correctly implements the "unconfigured" semantics, allowing the auto-enable behavior to work when outputs are later configured.


74-79: LGTM: Nullable backing field enables lazy initialization.

The nullable _logLevel backing field elegantly distinguishes between "unconfigured" (null → defaults to Off) and "explicitly disabled" (set to Off), enabling the auto-enable behavior when outputs are configured.


315-323: LGTM: Efficient early return.

The check at line 320 correctly short-circuits when there are no output destinations, avoiding unnecessary string formatting. Using the _logWriter backing field is appropriate here.

tests/NLog.UnitTests/Config/XmlConfigTests.cs (2)

47-64: LGTM: Test correctly validates unconfigured state.

The InternalLogger.Reset() call at line 51 ensures test isolation. The assertion at line 62 correctly verifies that when no internal logging outputs are configured, the log level remains Off (i.e., _logLevel is null and the property defaults to Off). The comment clarifies the expected behavior.


67-87: LGTM: Test correctly validates auto-enable behavior.

The InternalLogger.Reset() call at line 71 ensures clean state. The assertion at line 84 correctly verifies that when console outputs are enabled (line 75), the internal log level is auto-enabled to Info. This validates the core feature of this PR. The comment clarifies the expected behavior.

Comment on lines +167 to +180
public static TextWriter? LogWriter
{
get => _logWriter;
set
{
if (value != null)
{
if (_logLevel is null)
_logLevel = LogLevel.Info;
}
_logWriter = value;
}
}
private static TextWriter? _logWriter;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Backing field approach is sound; same race condition applies.

Using _logWriter as a backing field is correct and avoids re-triggering setter logic in WriteToLog. The auto-enable pattern at lines 174-175 has the same race condition as the other output setters.

🤖 Prompt for AI Agents
In src/NLog/Common/InternalLogger.cs around lines 167 to 180, the LogWriter
setter uses the backing field correctly but the auto-enable pattern (if
(_logLevel is null) _logLevel = LogLevel.Info;) is vulnerable to the same race
condition as other output setters; change this to perform the enable under the
same synchronization used elsewhere (either wrap the null-check-and-set in the
existing lock/sync object used by other setters or perform an atomic
compare-and-set on the underlying storage) so that only one thread can observe
and set _logLevel to Info; keep using the backing field _logWriter, but ensure
the _logLevel assignment is done atomically/under lock to avoid races.

@snakefoot snakefoot force-pushed the internallogger_info branch from 391ad42 to 71b385a Compare January 5, 2026 17:04
@snakefoot snakefoot force-pushed the internallogger_info branch from 71b385a to eaf866e Compare January 5, 2026 17:09
@pull-request-size pull-request-size bot added size/L and removed size/M labels Jan 5, 2026
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 5, 2026

@snakefoot snakefoot merged commit 5edb769 into NLog:dev Jan 31, 2026
5 of 6 checks passed
@snakefoot snakefoot added this to the 6.1.0 milestone Jan 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking behavior change Same API, different result size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant