feat(logs): add NLog integration - #5176
Merged
Merged
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5176 +/- ##
==========================================
+ Coverage 74.23% 77.31% +3.08%
==========================================
Files 509 421 -88
Lines 18435 15691 -2744
Branches 3610 3125 -485
==========================================
- Hits 13685 12132 -1553
+ Misses 3875 2826 -1049
+ Partials 875 733 -142 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Flash0ver
marked this pull request as ready for review
May 18, 2026 11:23
Flash0ver
commented
May 18, 2026
jamescrosswell
left a comment
Collaborator
There was a problem hiding this comment.
Overall looks pretty good. No blocking comments from me - mostly stylistic.
The main one that needs to be dealt with is the one that the Sentry bot called out since that would be a bug for certain initialisation paths.
The NLog target checked its own SentryNLogOptions.EnableLogs and passed those options to CaptureStructuredLog. When the SDK is initialized elsewhere (e.g. ASP.NET Core) with InitializeSdk = false, the target's own EnableLogs stays at its default, so structured logs were silently dropped even when the user enabled them on the actual SDK options. Read the options from the Hub instead, mirroring the Serilog sink. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: James Crosswell <james.crosswell@gmail.com>
Move the Create factory method into SentryLog.cs and drop the separate SentryLog.Factory.cs file (and its csproj DependentUpon entry). The method name makes it clear it's a factory, so a dedicated file isn't warranted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: James Crosswell <james.crosswell@gmail.com>
Use the ShouldContain/ShouldNotContain SentryAttributes extensions from Sentry.Testing (added in #4936) in place of the verbose TryGetAttribute(...).Should().BeTrue() pairs. Grant Sentry.NLog.Tests InternalsVisibleTo access to Sentry.Testing so the internal extensions are usable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: James Crosswell <james.crosswell@gmail.com>
CaptureStructuredLog never set the logger name on the structured log, so NLog structured logs lost the main source identifier used to filter and correlate in Sentry. Set the 'category.name' attribute from logEvent.LoggerName, mirroring the Microsoft.Extensions.Logging integration (and the NLog event path which sets Logger from the same value). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: James Crosswell <james.crosswell@gmail.com>
Unnamed message-template holes (e.g. `{}`) have an empty parameter name.
Stored as-is, the first hole serializes to `sentry.message.parameter.`
(empty trailing segment), and multiple genuinely-empty names collide on
that same attribute key so only the last value survives. Fall back to the
positional index when the name is empty, matching how other integrations
key unnamed parameters.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: James Crosswell <james.crosswell@gmail.com>
Guard only the two TFMs that lack the HashSet<T>(int capacity) constructor (netstandard2.0 and net462) instead of enumerating every framework that has it. net462 also lacks the constructor (added in net472), so a plain '!netstandard2.0' check would not compile there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: James Crosswell <james.crosswell@gmail.com>
main changed SentryLog.SetDefaultAttributes to (options, scope, sdk), where scope populates user.* and server.address attributes. The NLog structured path still called (options, sdk), binding SdkVersion into the Scope? parameter after merging main. Acquire the scope via hub.GetScope() and pass it explicitly, matching the Serilog and Extensions.Logging integrations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: James Crosswell <james.crosswell@gmail.com>
This was referenced Jul 27, 2026
evgenygunko
pushed a commit
to evgenygunko/CopyWordsDA
that referenced
this pull request
Jul 27, 2026
> ℹ️ **Note** > > This PR body was truncated due to platform limits. This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [Sentry.Maui](https://sentry.io/) ([source](https://github.com/getsentry/sentry-dotnet)) | `6.7.0` → `6.8.0` |  |  | --- ### Release Notes <details> <summary>getsentry/sentry-dotnet (Sentry.Maui)</summary> ### [`v6.8.0`](https://github.com/getsentry/sentry-dotnet/blob/HEAD/CHANGELOG.md#680) [Compare Source](getsentry/sentry-dotnet@6.7.0...6.8.0) ##### Features ✨ ##### Logs - feat(logs): add `log4net` integration by [@​Flash0ver](https://github.com/Flash0ver) in [#​5172](getsentry/sentry-dotnet#5172) - feat(logs): add `NLog` integration by [@​Flash0ver](https://github.com/Flash0ver) in [#​5176](getsentry/sentry-dotnet#5176) ##### Other - feat(serilog): support restrictedToMinimumLevel when configuring Serilog in code by [@​jamescrosswell](https://github.com/jamescrosswell) in [#​5181](getsentry/sentry-dotnet#5181) - Attachments can now be sent with transactions by setting `AddToTransactions` on `SentryAttachment` [#​5182](getsentry/sentry-dotnet#5182) by [@​jamescrosswell](https://github.com/jamescrosswell) in [#​5182](getsentry/sentry-dotnet#5182) - Added `SentrySdk.RecordTransaction` to record already-completed transactions and spans (e.g. replayed through a proxy) [#​5333](getsentry/sentry-dotnet#5333) by [@​jamescrosswell](https://github.com/jamescrosswell) in [#​5333](getsentry/sentry-dotnet#5333) - The `Environment` set on the `Scope` now gets synchronized to the native layers (`sentry-cocoa` and `sentry-native`) by [@​bitsandfoxes](https://github.com/bitsandfoxes) in [#​5365](getsentry/sentry-dotnet#5365) ##### Fixes 🐛 - fix: `SentrySpanProcessor` no longer leaks spans whose Activity never ends (e.g. aborted requests); the Activity is now held via a `WeakReference` so orphaned spans are pruned once it is garbage-collected. by [@​Ermabo](https://github.com/Ermabo) in [#​5393](getsentry/sentry-dotnet#5393) - The SDK was incorrectly ignoring server rate limits for errors, check-ins, and logs by [@​jamescrosswell](https://github.com/jamescrosswell) in [#​5412](getsentry/sentry-dotnet#5412) - fix: BackpressureMonitor.Dispose() no longer deadlocks on single-threaded targets by [@​jamescrosswell](https://git...
This was referenced Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #5167
Changes
Add
NLogintegration to Sentry Structured Logging.Docs
See also
Implementation notes
SentryTargetis an NLog target. Every log NLog routes to it lands inInnerWrite(SentryTarget.cs:331), which now fans out to two independent pipelines:logEvent→ Sentry event (ifLevel >= MinimumEventLevel) and/or breadcrumb.logEvent→ Sentry structured log, gated onEnableLogs.The two are additive and independently gated — enabling logs doesn't change event/breadcrumb behavior.
The dispatch gate
While the classic path continues to be gated by
MinimumEventLevel, a newEnableLogsproperty has been added to determine whether structured logs are created from NLog logs:NLog-specifics
CaptureStructuredLog(SentryTarget.Structured.cs) translates NLog'sLogEventInfointo aSentryLog.The NLog-specific concerns:
logEvent.Level(NLog.LogLevel)levelToSentryLogLevel()maps Trace/Debug/Info/Warn/Error/Fatal;Off→ null → drop (LogLevelExtensions.cs:33).logEvent.FormattedMessage(rendered)body(message)logEvent.Message(raw template)template"User {UserId} logged in".logEvent.MessageTemplateParametersparameters(→sentry.message.parameter.*)UserId. Note that NLog also allows unnamed parammeters to be passed like{}. These have an empty name soGetParametersfalls back to the positional index as the name, to avoid collisions.logEvent.Propertiesproperty.*attributesGetStructuredLoggingParametersAndAttributesfilters out null/blank keys and any key already used as a message parameter (!parameterNames.Contains(key)) so a value isn't emitted twice.logEvent.LoggerNamecategory.nameattributelogEvent.TimeStamp(DateTime)timestampDateTimeOffset.Everything else —
Environment,Release, SDK name/version,server.address,user.*— comes fromSetDefaultAttributes(options, scope, Sdk), shared with all integrations, plusSetOrigin("auto.log.nlog").