Skip to content

Conversation

@snakefoot
Copy link
Contributor

@snakefoot snakefoot commented Sep 7, 2025

Followup to #5741

@snakefoot snakefoot added this to the 6.0.4 milestone Sep 7, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 7, 2025

Walkthrough

Adds a safe exception-rendering helper in MessageLayoutRenderer that wraps Exception.ToString() with a try/catch and falls back to type/message plus recursive inner-exception rendering; also reorders two assignments in ExceptionLayoutRenderer.AppendToString's try block. No public API changes.

Changes

Cohort / File(s) Summary
Message exception rendering
src/NLog/LayoutRenderers/MessageLayoutRenderer.cs
Added AppendExceptionToString(StringBuilder, Exception) to safely render exceptions: attempts ToString() inside try/catch, logs a warning via InternalLogger on failure, falls back to "ExceptionType: message" and recursively appends inner exceptions. Replaced direct ToString() usage and added using NLog.Common. No public API changes.
Exception append ordering tweak
src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs
Reordered assignments inside AppendToString's try block so innerException = ex.InnerException is read before exceptionMessage = ex.Message. Behavior otherwise unchanged; no public API changes.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as MessageLayoutRenderer
    participant Helper as AppendExceptionToString
    participant Ex as Exception
    participant Log as InternalLogger

    Renderer->>Helper: AppendExceptionToString(builder, ex)
    alt ex.ToString() succeeds
        Helper->>Ex: ex.ToString()
        Ex-->>Helper: renderedString
        Helper-->>Renderer: append renderedString
    else ex.ToString() throws
        Helper->>Ex: ex.ToString()
        Ex--x Helper: throws
        Helper->>Log: Warn("Exception.ToString() failed", ex)
        Helper-->>Renderer: append "ExceptionType: message"
        opt inner exception present
            Helper->>Helper: AppendExceptionToString(builder, innerException)
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble logs with careful cheer,
When ToString trips, I persevere—
Type and message hop into view,
Inner stories follow through.
A tiny warning, soft and bright,
Safe strings stitched in moonlit light. 🐇

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

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: 0

🧹 Nitpick comments (2)
src/NLog/LayoutRenderers/MessageLayoutRenderer.cs (2)

95-103: Robust exception rendering path — good. One edge-case to verify.
If the message is empty (e.g., Raw with ""), ExceptionSeparator is still appended, producing a leading separator. Confirm this matches prior behavior; if not, gate the separator on whether this renderer appended any message content.

Possible tweak (requires capturing start length earlier in Append):

 protected override void Append(StringBuilder builder, LogEventInfo logEvent)
 {
+    int startLen = builder.Length;
@@
-                if (!exceptionOnly)
-                    builder.Append(ExceptionSeparator);
+                if (!exceptionOnly && builder.Length > startLen)
+                    builder.Append(ExceptionSeparator);
                 AppendExceptionToString(builder, primaryException);
 }

105-126: Fallback should include all AggregateException inners; trim allocations.
When ToString() throws on an AggregateException with multiple inners, only InnerException is rendered. Iterate InnerExceptions in the fallback and avoid interpolation to reduce temps.

Apply within the catch:

-            catch (Exception ex)
-            {
-                InternalLogger.Warn(ex, "Message-LayoutRenderer Could not output ToString for Exception: {0}", exception.GetType());
-                builder.Append($"{exception.GetType()}: {exceptionMessage}");
-                if (innerException != null)
-                {
-                    builder.AppendLine();
-                    AppendExceptionToString(builder, innerException);
-                }
-            }
+            catch (Exception ex)
+            {
+                InternalLogger.Warn(ex, "Message-LayoutRenderer failed to call ToString() on {0}", exception.GetType());
+                var typeName = exception.GetType().FullName ?? exception.GetType().Name;
+                builder.Append(typeName);
+                if (!string.IsNullOrEmpty(exceptionMessage))
+                {
+                    builder.Append(": ");
+                    builder.Append(exceptionMessage);
+                }
+
+                if (exception is AggregateException agg && agg.InnerExceptions?.Count > 0)
+                {
+                    foreach (var ie in agg.InnerExceptions)
+                    {
+                        builder.AppendLine();
+                        AppendExceptionToString(builder, ie);
+                    }
+                }
+                else if (innerException != null)
+                {
+                    builder.AppendLine();
+                    AppendExceptionToString(builder, innerException);
+                }
+            }

Also consider a simple recursion cap (e.g., max depth 16) to prevent pathological cycles.

Would you like a small unit test that defines an Exception whose ToString() throws and asserts the fallback (including multiple AggregateException inners)?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 198075a and 6408935.

📒 Files selected for processing (1)
  • src/NLog/LayoutRenderers/MessageLayoutRenderer.cs (2 hunks)
🔇 Additional comments (1)
src/NLog/LayoutRenderers/MessageLayoutRenderer.cs (1)

38-38: LGTM: InternalLogger import is appropriate.
Enables safe fallback logging inside the new exception renderer.

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: 0

🧹 Nitpick comments (1)
src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs (1)

441-446: Avoid losing the fallback Message when InnerException getter throws

Reordering means if ex.InnerException throws before exceptionMessage is captured, the fallback becomes Type: with an empty message. Capturing Message first preserves more signal in that failure mode.

Apply minimal swap:

-                innerException = ex.InnerException;
-                exceptionMessage = ex.Message;
+                exceptionMessage = ex.Message;
+                innerException = ex.InnerException;

Alternative (more robust): capture both properties in separate try/catch blocks before calling ToString() so either getter failing doesn’t hide the other.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6408935 and 09b9ec3.

📒 Files selected for processing (2)
  • src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs (1 hunks)
  • src/NLog/LayoutRenderers/MessageLayoutRenderer.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NLog/LayoutRenderers/MessageLayoutRenderer.cs

@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 7, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
64.3% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@snakefoot snakefoot merged commit cf8a7ca into NLog:dev Sep 7, 2025
5 of 6 checks passed
This was referenced Sep 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant