feat(logs): add log4net integration#5172
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5172 +/- ##
==========================================
+ Coverage 74.23% 74.61% +0.37%
==========================================
Files 509 512 +3
Lines 18435 18540 +105
Branches 3610 3617 +7
==========================================
+ Hits 13685 13833 +148
+ Misses 3875 3834 -41
+ Partials 875 873 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@Flash0ver I put this back in draft state until CI passes. Looks like there is some warden feedback worth checking as well. |
…n tests - Fall back to string.Empty when RenderedMessage is null, matching the existing non-structured path (review r3258513795). - Clear ThreadContext.Properties in test teardown so thread-static entries don't leak into subsequent tests (review r3258530109). - Adapt CaptureStructuredLog to the SetDefaultAttributes(options, scope, sdk) signature introduced on main, passing the active scope for enrichment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… extensions Use the SentryAttributes.ShouldContain extensions added in #4936 instead of the verbose TryGetAttribute/Should().Be pairs (review r3258494145). Grants Sentry.Log4Net.Tests access to Sentry.Testing internals. The collection assertion keeps BeEquivalentTo since the extension compares by value equality. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings the log4net structured-log path in line with the MEL integration, which sets category.name from the logger/category name. Previously the log4net logger name was only surfaced on the legacy SentryEvent.Logger. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sample only targets net481, so the #if NETFRAMEWORK conditional is always true. Remove it and keep the SetPrincipalPolicy call unconditionally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… file Move the Create factory method into SentryLog.cs, drop the now-unneeded partial modifier, and remove the SentryLog.Factory.cs DependentUpon entry from the csproj. A single method doesn't warrant its own file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Invert the level check into a pattern-matched guard clause that returns early, removing a level of nesting and folding the null check into the assignment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Matches the sibling GetLoggingEventProperties helper so structured logging doesn't rely on log4net's GetProperties() never returning null. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 13260a4. Configure here.
…umEventLevel Two structured-logging fixes: - Capture structured logs for every event again. The Append refactor in 13260a4 moved the EnableLogs block after the breadcrumb early-return, so events below MinimumEventLevel no longer produced structured logs. Move the capture ahead of the breadcrumb/event branching, restoring the original behavior (covered by DoAppend_StructuredLoggingWithoutException_LeavesBreadcrumb). - Apply the appender's Environment and SendIdentity settings to structured logs, overriding the scope/options defaults, to match the SentryEvent path. These are per-appender, opt-in settings, so SendIdentity mirrors the event path and is not additionally gated on SendDefaultPii. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… logs The SentryAppender's Environment and SendIdentity settings now apply to structured logs as well as events (getsentry/sentry-dotnet#5172). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

closes #4731
Changes
Add
log4netintegration to Sentry Structured Logging.Docs
See also
Implementation Notes
Sentry's log4net support is a log4net appender (
SentryAppender : AppenderSkeleton). Every log call flows throughAppend(LoggingEvent).Previously each event became either a
SentryEvent(error) or a breadcrumb. Now, whenoptions.EnableLogsis on,Appendalso callsCaptureStructuredLogto emit a Sentry structured log.log4net-specific stuff
1. Level mapping. log4net doesn't have a clean enum — it has ~18 named
Levelobjects (All, Finest, Verbose, Finer, Trace, Fine, Debug, Info, Notice, Warn, Error, Severe, Critical, Alert, Fatal, Emergency, …) plus user-defined custom levels with arbitrary integer values.ToSentryLogLevelcollapses all that into Sentry's 6 levels using>=threshold comparisons on the numeric value, so custom levels land in the nearest bucket andLevel.Offdrops the log (returnsnull).2. Message: rendered only. log4net pre-renders the final string into
LoggingEvent.RenderedMessage, which becomes the logMessage.3. No template, no parameters. log4net can't give us the original template of parameters:
log4net's message object is typically a
SystemStringFormatthat keeps the composite format string ("{0} {1}") and the args array as private fields with no public accessor. This is a limitation of log4net rather than Sentry's integration.4. Properties →
property.*attributes, with log4net-specific filtering. log4net merges event-level properties,ThreadContext.Properties,GlobalContext.Properties, and its own metadata into one bag viaGetProperties(). The loop (Structured.cs:20-29) maps eachDictionaryEntryto aproperty.<key>attribute, skipping:log4net:(log4net's internal entries — HostName, Identity, etc.),This
property.<key>convention matches Serilog, not MEL. (MEL doesn't have an ambient property bag — it emits parameters pluscategory.name/event.id/event.name.)