Skip to content

LogEventBuilder - Properties with IReadOnlyCollection for initialCapacity#6059

Merged
snakefoot merged 2 commits intoNLog:devfrom
snakefoot:LogEventBuilderProperties
Jan 31, 2026
Merged

LogEventBuilder - Properties with IReadOnlyCollection for initialCapacity#6059
snakefoot merged 2 commits intoNLog:devfrom
snakefoot:LogEventBuilderProperties

Conversation

@snakefoot
Copy link
Contributor

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Dec 7, 2025

Walkthrough

Adds a new public overload LogEventBuilder.Properties(IReadOnlyCollection<KeyValuePair<string, object?>>) under #if !NET35 && !NET40, with validation and pre-allocation; also adds an emptiness guard to the existing Properties(ReadOnlySpan<(string, object?)> properties) so it only processes non-empty spans.

Changes

Cohort / File(s) Summary
LogEventBuilder — properties API
src/NLog/LogEventBuilder.cs
Adds Properties(IReadOnlyCollection<KeyValuePair<string, object?>>) overload (under #if !NET35 && !NET40) with null validation, pre-allocation via CreatePropertiesInternal(initialCapacity: properties.Count), and assignment into internal properties. Adjusts Properties(ReadOnlySpan<(string, object?)> properties) to short-circuit when the span is empty (!properties.IsEmpty) and when _logEvent is null.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I found some keys and made a cozy row,
I sized the nest so each pair could grow,
I skipped the empty hops that led nowhere,
Tiny code burrows, tidy and fair,
Carrots of properties tucked in with care 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate relevance to the changeset. Add a pull request description explaining the purpose, benefits, and any relevant context for this performance optimization.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a Properties overload that accepts IReadOnlyCollection to enable initialCapacity optimization.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 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 9a46951 and 78cfff9.

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

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 LogEventBuilderProperties branch from 4586098 to 74ca3fc Compare December 7, 2025 20:12
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/LogEventBuilder.cs (1)

123-139: Use the returned dictionary instead of _logEvent.Properties to avoid unused-local and match the span overload

dictionary is currently assigned but never used, which will raise an “assigned but never used” warning and is inconsistent with the ReadOnlySpan<(string, object?)> overload below that writes through the returned dictionary. You can both remove the warning and be consistent by writing through dictionary:

-            if (_logEvent != null)
-            {
-                var dictionary = _logEvent.CreatePropertiesInternal(initialCapacity: properties.Count);
-                foreach (var property in properties)
-                    _logEvent.Properties[property.Key] = property.Value;
-            }
+            if (_logEvent != null)
+            {
+                var dictionary = _logEvent.CreatePropertiesInternal(initialCapacity: properties.Count);
+                foreach (var property in properties)
+                    dictionary[property.Key] = property.Value;
+            }

(Optionally, the existing IEnumerable<KeyValuePair<string, object?>> overload could detect IReadOnlyCollection<> and delegate to this overload for the same pre-sizing benefits, but that’s not required for this change.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 155b158 and 4586098.

📒 Files selected for processing (1)
  • src/NLog/LogEventBuilder.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/LogEventBuilder.cs (2)
src/NLog/ILoggerExtensions.cs (8)
  • LogEventBuilder (57-60)
  • LogEventBuilder (67-70)
  • LogEventBuilder (77-80)
  • LogEventBuilder (87-90)
  • LogEventBuilder (97-100)
  • LogEventBuilder (107-110)
  • LogEventBuilder (117-120)
  • LogEventBuilder (129-132)
src/NLog/Internal/Guard.cs (1)
  • Guard (56-78)

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/LogEventBuilder.cs (1)

123-139: Tighten null/Count check and actually use the preallocated dictionary

Nice addition for pre-sizing based on IReadOnlyCollection.Count. Two small tweaks would make this consistent with the other overload and avoid an “assigned but never used” local:

  • After Guard.ThrowIfNull(properties);, the properties?.Count null-conditional is redundant and inconsistent with the IEnumerable overload.
  • You allocate dictionary via CreatePropertiesInternal but then write through _logEvent.Properties; prefer using the local as in the span-based overload.

Suggested change:

-        public LogEventBuilder Properties(IReadOnlyCollection<KeyValuePair<string, object?>> properties)
-        {
-            Guard.ThrowIfNull(properties);
-            if (_logEvent != null && properties?.Count > 0)
-            {
-                var dictionary = _logEvent.CreatePropertiesInternal(initialCapacity: properties.Count);
-                foreach (var property in properties)
-                    _logEvent.Properties[property.Key] = property.Value;
-            }
-            return this;
-        }
+        public LogEventBuilder Properties(IReadOnlyCollection<KeyValuePair<string, object?>> properties)
+        {
+            Guard.ThrowIfNull(properties);
+            if (_logEvent != null && properties.Count > 0)
+            {
+                var dictionary = _logEvent.CreatePropertiesInternal(initialCapacity: properties.Count);
+                foreach (var property in properties)
+                    dictionary[property.Key] = property.Value;
+            }
+            return this;
+        }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4586098 and 74ca3fc.

📒 Files selected for processing (1)
  • src/NLog/LogEventBuilder.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/LogEventBuilder.cs (2)
src/NLog/ILoggerExtensions.cs (8)
  • LogEventBuilder (57-60)
  • LogEventBuilder (67-70)
  • LogEventBuilder (77-80)
  • LogEventBuilder (87-90)
  • LogEventBuilder (97-100)
  • LogEventBuilder (107-110)
  • LogEventBuilder (117-120)
  • LogEventBuilder (129-132)
src/NLog/Internal/Guard.cs (1)
  • Guard (56-78)
⏰ 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). (3)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (1)
src/NLog/LogEventBuilder.cs (1)

148-153: Early-exit on empty span looks good and avoids unnecessary allocations

The added !properties.IsEmpty guard is a clean micro-optimization and keeps behavior in line with the enumerable overload (no properties dictionary created when there are no properties). The rest of the method remains consistent with the new IReadOnlyCollection overload.

@snakefoot snakefoot force-pushed the LogEventBuilderProperties branch from 74ca3fc to d3a9f5b Compare December 7, 2025 20:19
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/LogEventBuilder.cs (1)

123-139: Good optimization with pre-allocation!

The new overload provides a nice performance improvement by pre-sizing the internal dictionary. The implementation is correct and consistent with the ReadOnlySpan overload.

One minor simplification: Line 131 uses properties?.Count with a null-conditional operator, but since Guard.ThrowIfNull(properties) on line 130 guarantees that properties is not null, you can simplify it to properties.Count.

Apply this diff to remove the redundant null check:

-            if (_logEvent != null && properties?.Count > 0)
+            if (_logEvent != null && properties.Count > 0)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74ca3fc and d3a9f5b.

📒 Files selected for processing (1)
  • src/NLog/LogEventBuilder.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/LogEventBuilder.cs (1)
src/NLog/Internal/Guard.cs (1)
  • Guard (56-78)
⏰ 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 (1)
src/NLog/LogEventBuilder.cs (1)

148-148: LGTM! Good optimization.

Adding the !properties.IsEmpty guard prevents unnecessary dictionary allocation when there are no properties to add. This is consistent with the new IReadOnlyCollection overload's count check.

@snakefoot snakefoot force-pushed the LogEventBuilderProperties branch from d3a9f5b to 6a4f6a5 Compare December 7, 2025 22:40
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/LogEventBuilder.cs (1)

123-139: IReadOnlyCollection overload looks correct and efficiently uses Count for pre-allocation

The new LogEventBuilder.Properties(IReadOnlyCollection<KeyValuePair<string, object?>> properties) overload is consistent with the existing API, uses Guard.ThrowIfNull, and safely short-circuits on _logEvent != null && properties.Count > 0 so Count is only accessed when needed. Pre-sizing via CreatePropertiesInternal(initialCapacity: properties.Count) plus a single enumeration is a nice optimization, and overload resolution will naturally route IReadOnlyCollection-typed callers here without affecting existing IEnumerable-based call sites.

As a minor, optional enhancement, the IEnumerable overload could forward to this one when properties is IReadOnlyCollection<KeyValuePair<string, object?>> ro to reuse the pre-allocation path, but that’s not required for this PR.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d3a9f5b and 6a4f6a5.

📒 Files selected for processing (1)
  • src/NLog/LogEventBuilder.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/NLog/LogEventBuilder.cs (2)
src/NLog/ILoggerExtensions.cs (8)
  • LogEventBuilder (57-60)
  • LogEventBuilder (67-70)
  • LogEventBuilder (77-80)
  • LogEventBuilder (87-90)
  • LogEventBuilder (97-100)
  • LogEventBuilder (107-110)
  • LogEventBuilder (117-120)
  • LogEventBuilder (129-132)
src/NLog/Internal/Guard.cs (1)
  • Guard (56-78)
⏰ 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 (1)
src/NLog/LogEventBuilder.cs (1)

148-153: Empty-span guard avoids unnecessary dictionary allocation

The added !properties.IsEmpty check in the ReadOnlySpan overload correctly prevents creating a properties dictionary when there are no items, aligning behavior with the new IReadOnlyCollection overload. This is a pure allocation optimization; the observable semantics for callers (no properties added when the span is empty) remain the same.

@snakefoot snakefoot force-pushed the LogEventBuilderProperties branch 2 times, most recently from 9a46951 to 0dd9315 Compare December 7, 2025 22:53
@snakefoot snakefoot force-pushed the LogEventBuilderProperties branch from 0dd9315 to 1bbaa24 Compare December 7, 2025 22:53
@sonarqubecloud
Copy link

@snakefoot snakefoot enabled auto-merge (squash) January 31, 2026 15:18
@snakefoot snakefoot disabled auto-merge January 31, 2026 15:18
@snakefoot snakefoot merged commit 66d08cf into NLog:dev Jan 31, 2026
5 of 6 checks passed
@snakefoot snakefoot added this to the 6.1.0 milestone Jan 31, 2026
@snakefoot snakefoot added enhancement Improvement on existing feature performance labels Jan 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement on existing feature performance size/S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant