Skip to content

feat(logs): add log4net integration - #5172

Merged
jamescrosswell merged 18 commits into
mainfrom
feat/logs-log4net
Jul 16, 2026
Merged

feat(logs): add log4net integration#5172
jamescrosswell merged 18 commits into
mainfrom
feat/logs-log4net

Conversation

@Flash0ver

@Flash0ver Flash0ver commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

closes #4731

Changes

Add log4net integration to Sentry Structured Logging.

Docs

See also

Implementation Notes

Sentry's log4net support is a log4net appender (SentryAppender : AppenderSkeleton). Every log call flows through Append(LoggingEvent).

Previously each event became either a SentryEvent (error) or a breadcrumb. Now, when options.EnableLogs is on, Append also calls CaptureStructuredLog to emit a Sentry structured log.

log4net-specific stuff

1. Level mapping. log4net doesn't have a clean enum — it has ~18 named Level objects (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. ToSentryLogLevel collapses all that into Sentry's 6 levels using >= threshold comparisons on the numeric value, so custom levels land in the nearest bucket and Level.Off drops the log (returns null).

2. Message: rendered only. log4net pre-renders the final string into LoggingEvent.RenderedMessage, which becomes the log Message.

3. No template, no parameters. log4net can't give us the original template of parameters:

const string? template = null;   // cannot get format-string from log4net.Util.SystemStringFormat
var parameters = ImmutableArray<...>.Empty; // cannot get arguments either

log4net's message object is typically a SystemStringFormat that 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 via GetProperties(). The loop (Structured.cs:20-29) maps each DictionaryEntry to a property.<key> attribute, skipping:

  • empty keys,
  • keys starting with log4net: (log4net's internal entries — HostName, Identity, etc.),
  • GUID-shaped keys (log4net's transient/internal correlation entries).

This property.<key> convention matches Serilog, not MEL. (MEL doesn't have an ambient property bag — it emits parameters plus category.name/event.id/event.name.)

@Flash0ver Flash0ver self-assigned this Apr 29, 2026
@codecov

codecov Bot commented May 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.82353% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.61%. Comparing base (b006e95) to head (4236d5f).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
src/Sentry.Log4Net/SentryAppender.cs 80.76% 3 Missing and 2 partials ⚠️
src/Sentry.Log4Net/LevelMapping.cs 75.00% 1 Missing and 2 partials ⚠️
src/Sentry.Log4Net/SentryAppender.Structured.cs 86.36% 1 Missing and 2 partials ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Flash0ver
Flash0ver marked this pull request as ready for review May 18, 2026 11:19
@Flash0ver
Flash0ver requested a review from jamescrosswell as a code owner May 18, 2026 11:19
Comment thread test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs Outdated
Comment thread src/Sentry.Log4Net/SentryAppender.Structured.cs Outdated
Comment thread test/Sentry.Log4Net.Tests/SentryAppenderTests.Structured.cs
@jamescrosswell

Copy link
Copy Markdown
Collaborator

@Flash0ver I put this back in draft state until CI passes. Looks like there is some warden feedback worth checking as well.

@jamescrosswell
jamescrosswell marked this pull request as draft June 23, 2026 01:24
jamescrosswell and others added 3 commits July 15, 2026 19:05
…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>
@jamescrosswell
jamescrosswell marked this pull request as ready for review July 15, 2026 22:55
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Jul 15, 2026
Comment thread src/Sentry.Log4Net/LevelMapping.cs
Comment thread samples/Sentry.Samples.Log4Net/Program.cs Outdated
jamescrosswell and others added 2 commits July 16, 2026 13:36
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>
Comment thread src/Sentry/SentryLog.Factory.cs Outdated
… 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>
Comment thread src/Sentry.Log4Net/SentryAppender.Structured.cs Outdated
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>
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` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Sentry.Maui/6.8.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Sentry.Maui/6.7.0/6.8.0?slim=true) |

---

### 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 [@&#8203;Flash0ver](https://github.com/Flash0ver) in [#&#8203;5172](getsentry/sentry-dotnet#5172)
- feat(logs): add `NLog` integration by [@&#8203;Flash0ver](https://github.com/Flash0ver) in [#&#8203;5176](getsentry/sentry-dotnet#5176)

##### Other

- feat(serilog): support restrictedToMinimumLevel when configuring Serilog in code by [@&#8203;jamescrosswell](https://github.com/jamescrosswell) in [#&#8203;5181](getsentry/sentry-dotnet#5181)
- Attachments can now be sent with transactions by setting `AddToTransactions` on `SentryAttachment` [#&#8203;5182](getsentry/sentry-dotnet#5182) by [@&#8203;jamescrosswell](https://github.com/jamescrosswell) in [#&#8203;5182](getsentry/sentry-dotnet#5182)
- Added `SentrySdk.RecordTransaction` to record already-completed transactions and spans (e.g. replayed through a proxy) [#&#8203;5333](getsentry/sentry-dotnet#5333) by [@&#8203;jamescrosswell](https://github.com/jamescrosswell) in [#&#8203;5333](getsentry/sentry-dotnet#5333)
- The `Environment` set on the `Scope` now gets synchronized to the native layers (`sentry-cocoa` and `sentry-native`) by [@&#8203;bitsandfoxes](https://github.com/bitsandfoxes) in [#&#8203;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 [@&#8203;Ermabo](https://github.com/Ermabo) in [#&#8203;5393](getsentry/sentry-dotnet#5393)
- The SDK was incorrectly ignoring server rate limits for errors, check-ins, and logs by [@&#8203;jamescrosswell](https://github.com/jamescrosswell) in [#&#8203;5412](getsentry/sentry-dotnet#5412)
- fix: BackpressureMonitor.Dispose() no longer deadlocks on single-threaded targets by [@&#8203;jamescrosswell](https://git...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Sentry Logs support to Log4Net

3 participants