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>
Comment thread src/Sentry.Log4Net/SentryAppender.Structured.cs
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>
Comment thread src/Sentry.Log4Net/SentryAppender.cs

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ 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.

Comment thread src/Sentry.Log4Net/SentryAppender.cs
…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>
jamescrosswell added a commit to getsentry/sentry-docs that referenced this pull request Jul 16, 2026
… 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>
Comment thread src/Sentry.Log4Net/SentryAppender.cs Outdated
@jamescrosswell
jamescrosswell merged commit ea85ec5 into main Jul 16, 2026
34 of 36 checks passed
@jamescrosswell
jamescrosswell deleted the feat/logs-log4net branch July 16, 2026 07:54
dingsdax added a commit to getsentry/sentry-docs that referenced this pull request Jul 20, 2026
## DESCRIBE YOUR PR

Documents the new log4net **structured logs** integration added in
getsentry/sentry-dotnet#5172, mirroring the existing Serilog logs
documentation and the sibling NLog logs docs PR (#18738).

### Changes
- **Enable the Logs page for the log4net guide** — removed
`dotnet.log4net` from the `notSupported` list in
`common/logs/index.mdx`, so `/platforms/dotnet/guides/log4net/logs/` now
renders.
- **log4net guide** (`guides/log4net/index.mdx`) — added a cross-link to
the Logs page.
- **Product Logs getting-started** — added log4net to the list of
supported SDKs.
- **`logs/*` platform-includes** (requirements, setup, usage, options,
integrations) — added log4net-specific content: enabling `EnableLogs`
via `SentrySdk.Init` (log4net's appender doesn't expose it), the
log4net→Sentry level mapping, and the `category.name`/`property.<name>`
attribute behavior.

### ⚠️ Needs confirmation
The **version in `requirements/dotnet.mdx` is a placeholder — `6.8.0`.**
The source PR (getsentry/sentry-dotnet#5172) isn't released yet (current
released version is 6.6.0), so please correct this to whatever release
actually ships the `Sentry.Log4Net` structured-logs feature.

### Notes
- Faithful port of the Serilog/NLog logs docs onto log4net — same
structure, `PlatformSection` gating, and phrasing.
- log4net-specific caveat documented: because log4net renders messages
before they reach the appender, its structured logs carry the formatted
message but **no template or parameters** (unlike Serilog/NLog).
- Should merge only once/after getsentry/sentry-dotnet#5172 lands and
its release version is known.

Related: getsentry/sentry-dotnet#5172

## IS YOUR CHANGE URGENT?

Help us prioritize incoming PRs by letting us know when the change needs
to go live.
- [ ] Urgent deadline (GA date, etc.): 
- [ ] Other deadline: 
- [x] None: Not urgent, can wait up to 1 week+

## SLA

- Teamwork makes the dream work, so please add a reviewer to your PRs.
- Please give the docs team up to 1 week to review your PR unless you've
added an urgent due date to it.
Thanks in advance for your help!

## PRE-MERGE CHECKLIST

*Make sure you've checked the following before merging your changes:*

- [ ] Checked Vercel preview for correctness, including links
- [ ] PR was reviewed and approved by any necessary SMEs (subject matter
experts)
- [ ] PR was reviewed and approved by a member of the [Sentry docs
team](https://github.com/orgs/getsentry/teams/docs)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Johannes Daxböck <johannes.daxboeck@sentry.io>
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