Description
Sentry.Log4Net is not compatible with log4net 3.x: every log4net event property is silently dropped from structured logs, so no property.* attributes are ever attached to a SentryLog. There is no exception and no warning — the attributes simply aren't there.
This affects the shipped package, not just the test suite. Sentry.Log4Net compiles against log4net 2.0.12, and log4net 3.x loads happily against that assembly, so any user on log4net 3.x with EnableLogs loses all their event properties today.
Root cause
SentryAppender.Structured.cs:43-45 enumerates the properties dictionary and pattern-matches each element as a DictionaryEntry:
foreach (var property in properties)
{
if (property is DictionaryEntry { Key: string key, Value: { } value })
That shape changed between log4net majors. In 2.x, log4net.Util.PropertiesDictionary implements only the non-generic IDictionary, so IEnumerable.GetEnumerator() yields boxed DictionaryEntry. In 3.x it additionally implements IDictionary<string, object?>, and the non-generic enumerator now yields boxed KeyValuePair<string, object?>. Confirmed by reflecting over both packages:
log4net 2.0.12 PropertiesDictionary : ISerializable, IDictionary, ICollection, IEnumerable
IEnumerable.GetEnumerator() element -> System.Collections.DictionaryEntry
log4net 3.4.0 PropertiesDictionary : IEmptyInterface, IDictionary, ICollection, IEnumerable,
IDictionary<string, object>, ICollection<KVP>, IEnumerable<KVP>
IEnumerable.GetEnumerator() element -> System.Collections.Generic.KeyValuePair<string, object>
Because the already-compiled appender walks the non-generic enumerator, the is DictionaryEntry test never matches under 3.x, the loop body never runs, and no attribute is set.
Worth noting for whoever picks this up: bumping the PackageReference alone doesn't fix it — it converts the silent failure into a compile error, because foreach (var property in properties) then binds to the generic IEnumerable<KeyValuePair<string, object?>>:
error CS8121: An expression of type 'KeyValuePair<string, object?>' cannot be handled by a pattern of type 'DictionaryEntry'
The SentryEvent path is unaffected: GetLoggingEventProperties reads via GetKeys() + the indexer, which behave identically on both majors.
Reproduction
In a checkout of main, change test/Sentry.Log4Net.Tests/Sentry.Log4Net.Tests.csproj from <PackageReference Include="log4net" Version="2.0.15" /> to Version="3.4.0", then:
dotnet test test/Sentry.Log4Net.Tests
Three tests fail (55 pass), all in the structured-logging path:
SentryAppenderTests.DoAppend_StructuredLogging_Properties — "Expected log.Attributes to contain a single item matching attribute.Key.StartsWith("property."), but no such item was found."
SentryAppenderTests.DoAppend_StructuredLogging_LogEvent(withActiveSpan: False)
SentryAppenderTests.DoAppend_StructuredLogging_LogEvent(withActiveSpan: True) — both fail asserting property.Text-Property via test/Sentry.Testing/SentryAttributesExtensions.cs:7.
Suggested fix
Read the dictionary through GetKeys() + the indexer, the way the sibling GetLoggingEventProperties already does. That API is stable across both majors, so the appender stays version-agnostic and keeps working whether the consumer resolves log4net 2.x or 3.x.
Two follow-ups worth deciding at the same time:
- Add coverage for log4net 3.x (a test matrix over both majors, or a second test project) so this can't silently regress again.
- Decide whether
src/Sentry.Log4Net's own PackageReference floor should move off 2.0.12 — separate from the fix above, since the fix should make the appender work on both without raising the floor.
Found while investigating log4net 3.x support; root cause verified locally against log4net 2.0.12 and 3.4.0 at 0004ef86.
Description
Sentry.Log4Netis not compatible with log4net 3.x: every log4net event property is silently dropped from structured logs, so noproperty.*attributes are ever attached to aSentryLog. There is no exception and no warning — the attributes simply aren't there.This affects the shipped package, not just the test suite.
Sentry.Log4Netcompiles against log4net 2.0.12, and log4net 3.x loads happily against that assembly, so any user on log4net 3.x withEnableLogsloses all their event properties today.Root cause
SentryAppender.Structured.cs:43-45enumerates the properties dictionary and pattern-matches each element as aDictionaryEntry:That shape changed between log4net majors. In 2.x,
log4net.Util.PropertiesDictionaryimplements only the non-genericIDictionary, soIEnumerable.GetEnumerator()yields boxedDictionaryEntry. In 3.x it additionally implementsIDictionary<string, object?>, and the non-generic enumerator now yields boxedKeyValuePair<string, object?>. Confirmed by reflecting over both packages:Because the already-compiled appender walks the non-generic enumerator, the
is DictionaryEntrytest never matches under 3.x, the loop body never runs, and no attribute is set.Worth noting for whoever picks this up: bumping the
PackageReferencealone doesn't fix it — it converts the silent failure into a compile error, becauseforeach (var property in properties)then binds to the genericIEnumerable<KeyValuePair<string, object?>>:The
SentryEventpath is unaffected:GetLoggingEventPropertiesreads viaGetKeys()+ the indexer, which behave identically on both majors.Reproduction
In a checkout of
main, changetest/Sentry.Log4Net.Tests/Sentry.Log4Net.Tests.csprojfrom<PackageReference Include="log4net" Version="2.0.15" />toVersion="3.4.0", then:Three tests fail (55 pass), all in the structured-logging path:
SentryAppenderTests.DoAppend_StructuredLogging_Properties— "Expected log.Attributes to contain a single item matchingattribute.Key.StartsWith("property."), but no such item was found."SentryAppenderTests.DoAppend_StructuredLogging_LogEvent(withActiveSpan: False)SentryAppenderTests.DoAppend_StructuredLogging_LogEvent(withActiveSpan: True)— both fail assertingproperty.Text-Propertyviatest/Sentry.Testing/SentryAttributesExtensions.cs:7.Suggested fix
Read the dictionary through
GetKeys()+ the indexer, the way the siblingGetLoggingEventPropertiesalready does. That API is stable across both majors, so the appender stays version-agnostic and keeps working whether the consumer resolves log4net 2.x or 3.x.Two follow-ups worth deciding at the same time:
src/Sentry.Log4Net's ownPackageReferencefloor should move off 2.0.12 — separate from the fix above, since the fix should make the appender work on both without raising the floor.Found while investigating log4net 3.x support; root cause verified locally against log4net 2.0.12 and 3.4.0 at
0004ef86.