-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Logger skipping parameters-array allocation when properties #5804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 2 minutes and 31 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 (10)
WalkthroughThis set of changes enhances the NLog logging framework to support logging methods that accept Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant Logger
participant LogMessageTemplateFormatter
participant LogFactory
participant Targets
Caller->>Logger: Log(LogLevel, string message, ReadOnlySpan<object> args)
alt .NET Standard 2.1+ or .NET 9.0+
Logger->>Logger: WriteToTargetsWithSpan(...)
Logger->>LogFactory: Check LogMessageTemplateFormatter
alt Preformatting needed
Logger->>LogMessageTemplateFormatter: Render(..., ReadOnlySpan<object> args)
LogMessageTemplateFormatter-->>Logger: Formatted message
Logger->>Targets: Write formatted message
else
Logger->>Targets: Write with deferred formatting
end
else
Logger->>Logger: WriteToTargets(..., object[] args)
Logger->>Targets: Write message
end
Assessment against linked issues
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:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🔭 Outside diff range comments (1)
src/NLog/Logger-generated.cs (1)
188-199:⚠️ Potential issueCompilation blocker replicated in generated code
All span-based overloads emitted by the T4 template keep the invalid signature:
public void Trace(string message, params ReadOnlySpan<object> args)This prevents the entire assembly from compiling.
Fix in the template and regenerate rather than editing this file directly.Additionally, make sure every internal call site is updated:
- WriteToTargetsWithSpan(..., message, args); + WriteToTargetsWithSpan(..., message, args); // unchanged(the call itself is fine once the signature is corrected).
Also applies to: 194-199
♻️ Duplicate comments (2)
src/NLog/Logger-V1Compat.cs (2)
127-133: Duplicate of the first comment – same pattern, same concern.
506-512: Duplicate of the first comment – same pattern, same concern.
🧹 Nitpick comments (2)
src/NLog/Logger-generated.cs (1)
274-279: Unnecessary stack allocation fallback still allocates on heapIn the
#elsebranch you wrap a single generic argument intonew object[] { argument }.
Because logging is disabled most of the time, consider replacing this with:- WriteToTargets(..., new object[] { argument }); + WriteToTargets(..., ArrayHelper.Wrap(argument)); // or pooled array / stackallocto avoid per-call heap allocation on old TFMs.
src/NLog/Internal/LogMessageTemplateFormatter.cs (1)
299-307: Minor: collapse duplicate overloads
RenderHolePositionalandRenderHoleshare the same early-null path.
You can remove duplication by letting one call the other after the null-check.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/NLog/ILoggerExtensions.cs(6 hunks)src/NLog/Internal/LogMessageTemplateFormatter.cs(6 hunks)src/NLog/LogEventInfo.cs(2 hunks)src/NLog/LogFactory.cs(2 hunks)src/NLog/Logger-V1Compat.cs(30 hunks)src/NLog/Logger-generated.cs(48 hunks)src/NLog/Logger-generated.tt(8 hunks)src/NLog/Logger.cs(15 hunks)src/NLog/MessageTemplates/LiteralHole.cs(1 hunks)src/NLog/MessageTemplates/TemplateEnumerator.cs(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/NLog/MessageTemplates/LiteralHole.cs (2)
src/NLog/MessageTemplates/Literal.cs (1)
Literal(52-56)src/NLog/MessageTemplates/Hole.cs (1)
Hole(48-55)
src/NLog/ILoggerExtensions.cs (1)
src/NLog/Logger-generated.cs (10)
Debug(403-409)Debug(417-423)Debug(429-436)Debug(457-463)Debug(515-521)Trace(111-117)Trace(125-131)Trace(137-144)Trace(165-171)Trace(223-229)
src/NLog/Internal/LogMessageTemplateFormatter.cs (3)
src/NLog/MessageTemplates/TemplateEnumerator.cs (2)
MoveNext(105-136)TemplateEnumerator(65-72)src/NLog/MessageTemplates/Hole.cs (1)
Hole(48-55)src/NLog/MessageTemplates/ValueFormatter.cs (1)
FormatToString(378-401)
🔇 Additional comments (18)
src/NLog/MessageTemplates/TemplateEnumerator.cs (1)
57-57: LGTM - Adding property accessor for template string.The newly added
Templateproperty provides external access to the original template string being parsed, which can be useful for components that need to access the raw template string directly.src/NLog/MessageTemplates/LiteralHole.cs (1)
59-59: Good optimization for struct parameter passing.Using the
inkeyword for struct parameters is a performance improvement that avoids copying the struct values when passed to the constructor. SinceLiteralandHoleare structs, this reduces memory allocations and improves performance in code paths that create manyLiteralHoleinstances.src/NLog/ILoggerExtensions.cs (6)
255-255: LGTM - Using generic overload instead of object array.This change improves performance by calling the generic
Debug<object>method directly instead of packing arguments into a temporary object array. This reduces memory allocations, especially when logging is disabled.
274-274: LGTM - Using generic overload for multiple arguments.Similar optimization for the two-argument case, calling
Debug<object, object>instead of creating an object array.
295-295: LGTM - Using generic overload for three arguments.Similar optimization for the three-argument case, using
Debug<object, object, object>to avoid array allocations.
422-422: LGTM - Using generic Trace overload instead of object array.The same optimizations applied to the Trace method, replacing object array creation with direct generic method calls.
441-441: LGTM - Using generic Trace overload for two arguments.Similar optimization for the two-argument case for Trace calls.
462-462: LGTM - Using generic Trace overload for three arguments.Similar optimization for the three-argument case for Trace calls.
src/NLog/LogFactory.cs (3)
72-72: LGTM - Adding field for caching LogMessageTemplateFormatter.Adding an internal field to cache the LogMessageTemplateFormatter instance, which will support the span-based optimization improvements.
309-312: LGTM - Conditionally caching the message formatter.This improvement caches the LogMessageTemplateFormatter only when message template parsing is disabled. When parsing is enabled, it sets the cache to null since each message might need different parsing logic. The SingleTargetMessageFormatter is always assigned regardless of the EnableMessageTemplateParser setting.
316-316: LGTM - Proper null handling in else branch.Ensures the LogMessageTemplateFormatter field is explicitly set to null when the messageFormatter is not a LogMessageTemplateFormatter.
src/NLog/LogEventInfo.cs (2)
612-629: Approve the refactored method for enhanced readability.The refactoring of
NeedToPreformatMessage(object[] parameters)improves readability with early returns and clearer control flow while maintaining the same functionality.
631-648: Well-implemented span-based performance optimization.This new overload leverages
ReadOnlySpan<object>with theinmodifier to avoid unnecessary array allocations, particularly beneficial when logging is disabled. The conditional compilation ensures compatibility with older frameworks while allowing modern .NET applications to benefit from this optimization.src/NLog/Logger-V1Compat.cs (2)
722-724: Good use of the new span overloadHere the traditional
IsTraceEnabledguard is retained, so no unnecessary work is done when the level is disabled. 👍
98-104:Details
✅ Verification successful
Consider restoring the
IsEnabled(level)guard (affects every newWriteToTargetsWithSpancall)For the span-based branch we now gate on
var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { ... }where the legacy code used a cheap
IsEnabled(level)check.If
GetTargetsForLevelSafeinternally performs the same check this is fine, but if it merely returns an empty array the extra lookup/allocation happens for every disabled log-call, partly defeating the allocation-saving goal of this PR.+#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + if (IsEnabled(level)) // keeps the fast short-circuit + { + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, + Factory.DefaultCultureInfo, message, arg1, arg2); + } + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new[] { arg1, arg2 }); } -#endif +#endifPlease verify the behaviour of
GetTargetsForLevelSafe(and whether it allocates) before applying – the same pattern recurs in many overloads.
🏁 Script executed:
#!/bin/bash set -e # Locate and display the implementation of GetTargetsForLevelSafe in Logger-V1Compat.cs rg -n "GetTargetsForLevelSafe" -A15 -B5 src/NLog/Logger-V1Compat.csLength of output: 3758
🏁 Script executed:
#!/bin/bash set -e # Search for the definition of GetTargetsForLevelSafe across the codebase rg -n "GetTargetsForLevelSafe" -g "*.cs" -A50 -B5Length of output: 44095
No changes needed for the span-based guard
Verified that
IsEnabled(level)is implemented aspublic bool IsEnabled(LogLevel level) => GetTargetsForLevelSafe(level) != null;and
GetTargetsForLevelSafesimply does an array lookup (no allocations). The new code already invokesGetTargetsForLevelSafeexactly once and short-circuits onnull, matching the legacy behavior without extra overhead.src/NLog/Logger.cs (3)
254-254: Good refactor – avoids one virtual-call hopReplacing the direct
WriteToTargetsinvocation with the slimmerWriteLogEventToTargetshelper removes an extra condition/overload resolution step and keeps all target dispatch logic in one place. Nice micro-cleanup.
428-456: Potential infinite recursion / ambiguous overload after signature changeBoth overloads of
WriteToTargetsWithSpancall each other in certain
paths, e.g.:WriteToTargetsWithSpan(level, …) ➜ WriteToTargetsWithSpan(targetsForLevel, …)After the signature corrections, ensure
(1) the first overload is only a convenience wrapper that retrieves
targetsForLeveland does not re-enter the same overload chain, and
(2) there is no accidental overload hiding onceparamsis removed.A quick guard:
private void WriteToTargetsWithSpan(LogLevel level,…) { var targets = GetTargetsForLevel(level); if (targets is null) return; // NOTE: do not call itself again – call the <ITargetWithFilterChain,…> variant WriteToTargetsWithSpan(targets, level, …); }Validate with unit tests or Roslyn analyzers to avoid stack-overflow.
805-806: Consistent use of the new helper—niceEvery internal
WriteToTargetsoverload has been updated to delegate to
WriteLogEventToTargets, centralising common exception handling and
context property merging. This improves maintainability and reduces code
duplication.Also applies to: 817-818, 827-828, 840-841, 850-851
| public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan<object> args) | ||
| { | ||
| var targetsForLevel = GetTargetsForLevel(LogLevel.<#=level#>); | ||
| if (targetsForLevel != null) | ||
| if (Is<#=level#>Enabled) | ||
| { | ||
| var logEvent = LogEventInfo.Create(LogLevel.<#=level#>, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); | ||
| WriteToTargets(logEvent, targetsForLevel); | ||
| WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, args); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
params ReadOnlySpan<object> is illegal C# syntax – will not compile
params can only be applied to array types (T[]).
Using it with ReadOnlySpan<T> causes a compiler error (CS0225).
This mistake is repeated for every span‐based overload generated by the template.
- public void <#=level#>(..., params ReadOnlySpan<object> args)
+ public void <#=level#>(..., ReadOnlySpan<object> args)Please remove the params modifier (and regenerate the .cs file), or switch back to object[] if you need the params behaviour.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan<object> args) | |
| { | |
| var targetsForLevel = GetTargetsForLevel(LogLevel.<#=level#>); | |
| if (targetsForLevel != null) | |
| if (Is<#=level#>Enabled) | |
| { | |
| var logEvent = LogEventInfo.Create(LogLevel.<#=level#>, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); | |
| WriteToTargets(logEvent, targetsForLevel); | |
| WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, args); | |
| } | |
| } | |
| public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, ReadOnlySpan<object> args) | |
| { | |
| if (Is<#=level#>Enabled) | |
| { | |
| WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, args); | |
| } | |
| } |
src/NLog/Logger.cs
Outdated
| [MessageTemplateFormatMethod("message")] | ||
| public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan<object> args) | ||
| { | ||
| var targetsForLevel = GetTargetsForLevel(level); | ||
| var targetsForLevel = GetTargetsForLevelSafe(level); | ||
| if (targetsForLevel != null) | ||
| { | ||
| var logEvent = LogEventInfo.Create(level, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); | ||
| WriteToTargets(logEvent, targetsForLevel); | ||
| WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, args); | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
params ReadOnlySpan<object> is illegal – will not compile
C# currently only permits params on single-dimension arrays.
Declaring or consuming params ReadOnlySpan<T> therefore yields CS0225 / CS1736.
All affected members:
Log(LogLevel, string, params ReadOnlySpan<object> args)Log(LogLevel, Exception, string, params ReadOnlySpan<object> args)WriteToTargetsWithSpan(…)(both overloads)
will break the build on every target framework.
Suggested fix (example for the first overload – apply similarly to the rest):
- public void Log(LogLevel level, string message, params ReadOnlySpan<object> args)
+ public void Log(LogLevel level, string message, ReadOnlySpan<object> args)and drop the params modifier from every call-site & method definition that uses a ReadOnlySpan<T>.
If a call-site must forward n standalone arguments, compose the span explicitly:
var tmp = new object[] { arg1, arg2 }; // unavoidable allocation
Log(level, message, tmp); // implicit array->spanor stay on the existing object[] overloads.
Failing to address this will make the library uncompilable.
Also applies to: 410-417, 419-456
8d91243 to
59bdd55
Compare
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/NLog/ILoggerExtensions.cs(6 hunks)src/NLog/Internal/LogMessageTemplateFormatter.cs(5 hunks)src/NLog/LogEventInfo.cs(2 hunks)src/NLog/LogFactory.cs(2 hunks)src/NLog/Logger-V1Compat.cs(30 hunks)src/NLog/Logger-generated.cs(48 hunks)src/NLog/Logger-generated.tt(8 hunks)src/NLog/Logger.cs(14 hunks)src/NLog/MessageTemplates/LiteralHole.cs(1 hunks)src/NLog/MessageTemplates/TemplateEnumerator.cs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
- src/NLog/MessageTemplates/TemplateEnumerator.cs
- src/NLog/MessageTemplates/LiteralHole.cs
- src/NLog/ILoggerExtensions.cs
- src/NLog/Logger-generated.tt
- src/NLog/LogEventInfo.cs
- src/NLog/LogFactory.cs
- src/NLog/Logger-V1Compat.cs
- src/NLog/Logger-generated.cs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/NLog/Logger.cs (3)
src/NLog/Logger-generated.cs (16)
MessageTemplateFormatMethod(152-159)MessageTemplateFormatMethod(178-185)MessageTemplateFormatMethod(193-200)MessageTemplateFormatMethod(208-215)MessageTemplateFormatMethod(237-244)MessageTemplateFormatMethod(253-260)MessageTemplateFormatMethod(269-280)MessageTemplateFormatMethod(288-299)MessageTemplateFormatMethod(310-321)MessageTemplateFormatMethod(331-342)MessageTemplateFormatMethod(355-366)MessageTemplateFormatMethod(378-389)MessageTemplateFormatMethod(444-451)MessageTemplateFormatMethod(470-477)MessageTemplateFormatMethod(485-492)MessageTemplateFormatMethod(500-507)src/NLog/Internal/LogMessageTemplateFormatter.cs (4)
LogMessageTemplateFormatter(44-333)LogMessageTemplateFormatter(65-71)Render(139-207)Render(210-278)src/NLog/LogEventInfo.cs (12)
LogEventInfo(51-795)LogEventInfo(79-82)LogEventInfo(90-93)LogEventInfo(102-116)LogEventInfo(126-131)LogEventInfo(141-148)LogEventInfo(159-162)LogEventInfo(173-182)LogEventInfo(444-447)LogEventInfo(456-459)LogEventInfo(484-496)LogEventInfo(507-510)
🔇 Additional comments (8)
src/NLog/Internal/LogMessageTemplateFormatter.cs (5)
55-55: LGTM: Improved naming consistency with_forceMessageTemplateRendererThe rename from
_forceTemplateRendererto_forceMessageTemplateRendererimproves naming consistency and better reflects its purpose in relation to message templates.Also applies to: 62-69, 78-78
141-207: RefactoredRendermethod for better separation of positional and non-positional holesThe refactoring creates a cleaner structure with better logical separation between positional and named template holes. The new flow correctly determines the message format type and handles template parameters accordingly.
Some key improvements:
- Initializing
messageTemplateParameterstonullupfront- Clearly separating the positional template path from the named template path
- Refactoring the shared hole rendering logic to the new
RenderHolePositionalmethod- Adding proper handling for rewind scenarios
- Improved null checks when truncating parameters
209-279: LGTM: New span-basedRenderoverload for modern .NET versionsThe new span-based rendering method is a good optimization that leverages
ReadOnlySpan<object>to reduce allocations. It maintains the same logical structure as the array-based version while properly utilizing spans.
281-295: Good extraction of positional hole rendering logicExtracting the positional hole rendering logic into a separate method is a good refactoring that improves code maintainability and readability.
297-312: LGTM: SimplifiedRenderHolemethodsThe refactored
RenderHolemethods provide a cleaner separation of concerns, with the primary method delegating to a more specialized implementation.src/NLog/Logger.cs (3)
254-254: LGTM: Improved naming clarity withWriteLogEventToTargetsThe rename from
WriteToTargetstoWriteLogEventToTargetsprovides better clarity about the method's purpose - specifically writing a LogEventInfo object to targets.Also applies to: 272-272, 803-803, 815-815, 825-825, 838-838, 848-848, 852-890
413-425: LGTM: Conditional span-based logging for generic argumentsThe conditional compilation approach allows efficient span-based logging for modern .NET frameworks while maintaining backward compatibility with older frameworks. The direct use of
WriteToTargetsWithSpanfor generic arguments avoids unnecessary array allocations.Also applies to: 437-449, 464-476, 490-502, 519-531, 547-559
595-602: LGTM: Smart message formatting optimizationThe
WriteToTargetsWithSpanmethod intelligently determines whether to:
- Defer message formatting completely
- Convert parameters to properties for non-positional templates (skipping array allocation)
- Fall back to traditional string.Format with array allocation for positional templates
This optimization reduces unnecessary allocations in high-throughput logging scenarios.
However, similar to the public methods, remove the
paramsmodifier:- private void WriteToTargetsWithSpan(LogLevel level, Exception exception, IFormatProvider formatProvider, string message, params ReadOnlySpan<object> args) + private void WriteToTargetsWithSpan(LogLevel level, Exception exception, IFormatProvider formatProvider, string message, ReadOnlySpan<object> args)- private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogLevel level, Exception exception, IFormatProvider formatProvider, string message, params ReadOnlySpan<object> args) + private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogLevel level, Exception exception, IFormatProvider formatProvider, string message, ReadOnlySpan<object> args)Also applies to: 604-632
ce99697 to
12f5623
Compare
There was a problem hiding this 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/Internal/LogMessageTemplateFormatter.cs (1)
141-279: Consider extracting common rendering logicThere's significant duplication between the array-based and span-based
Rendermethods. Consider extracting the common template processing logic into a shared private method to improve maintainability.You could create a generic helper method that accepts the template enumerator, format provider, and a callback for parameter handling:
private void RenderTemplate<T>(ref TemplateEnumerator enumerator, IFormatProvider formatProvider, T parameters, StringBuilder sb, ref int holeIndex, ref IList<MessageTemplateParameter> messageTemplateParameters, Func<int, T, object> getParameter, Action resetAction) { string template = enumerator.Template; int pos = 0; int holeStartPosition = 0; do { var literal = enumerator.Current.Literal; sb.Append(template, pos, literal.Print); pos += literal.Print; if (literal.Skip == 0) { pos++; continue; } pos += literal.Skip; var hole = enumerator.Current.Hole; // Rest of the common logic... } while (enumerator.MoveNext()); }Then both
Rendermethods could call this helper with appropriate parameters and callbacks.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/NLog/ILoggerExtensions.cs(6 hunks)src/NLog/Internal/LogMessageTemplateFormatter.cs(5 hunks)src/NLog/LogEventInfo.cs(2 hunks)src/NLog/LogFactory.cs(2 hunks)src/NLog/Logger-V1Compat.cs(30 hunks)src/NLog/Logger-generated.cs(48 hunks)src/NLog/Logger-generated.tt(8 hunks)src/NLog/Logger.cs(14 hunks)src/NLog/MessageTemplates/LiteralHole.cs(1 hunks)src/NLog/MessageTemplates/TemplateEnumerator.cs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
- src/NLog/MessageTemplates/TemplateEnumerator.cs
- src/NLog/MessageTemplates/LiteralHole.cs
- src/NLog/ILoggerExtensions.cs
- src/NLog/LogFactory.cs
- src/NLog/LogEventInfo.cs
- src/NLog/Logger-generated.tt
- src/NLog/Logger-generated.cs
- src/NLog/Logger-V1Compat.cs
- src/NLog/Logger.cs
🔇 Additional comments (4)
src/NLog/Internal/LogMessageTemplateFormatter.cs (4)
55-56: Good name improvement for clarityThe renaming of the field and parameter from
_forceTemplateRendererto_forceMessageTemplateRendererimproves clarity by aligning with MessageTemplate terminology used throughout the codebase. TheEnableMessageTemplateParserproperty update is consistent with this change.Also applies to: 62-63, 68-69, 78-78
141-207: Improved message template rendering logicThe refactored
Rendermethod provides several improvements:
- Better structure with early exit for non-message-template formats
- Improved handling of mixed format templates with rewind capability
- Proper null initialization and cleanup for parameter arrays
- Do-while loop provides clearer control flow
The implementation now handles both positional and named parameters more efficiently, with proper rewinding when mixed templates are detected.
209-279: Good optimization for modern .NET platformsThe span-based overload for .NET Standard 2.1+ and .NET 9.0+ provides performance benefits by:
- Accepting
ReadOnlySpan<object>to avoid array allocations- Using the pooled StringBuilder pattern efficiently
- Providing equivalent functionality to the array-based version
The null-check on line 268 properly prevents the potential
NullReferenceExceptionidentified in a previous review.
281-295: Well-structured method breakdown for hole renderingThe separation of rendering logic into distinct methods improves code organization:
- New
RenderHolePositionalmethod handles the specific case of positional holes- Refactored
RenderHolemethods provide clear overloads for different parameter types- Common rendering logic is properly shared
This structure supports both the original array-based and new span-based implementations efficiently.
Also applies to: 297-300, 302-312
12f5623 to
5a720db
Compare
|




Followup to #5800 to resolve #5537
When NLog needs to render message template upfront, then it will now skip Parameters-array-allocation, and instead rely on Properties-dictionary (Now matches NLog.Extensions.Logging)