Skip to content

Backport: Add native semantic logging support with property extraction#7955

Merged
Aaronontheweb merged 1 commit intoakkadotnet:v1.5from
Aaronontheweb:backport/semantic-logging-v1.5
Dec 2, 2025
Merged

Backport: Add native semantic logging support with property extraction#7955
Aaronontheweb merged 1 commit intoakkadotnet:v1.5from
Aaronontheweb:backport/semantic-logging-v1.5

Conversation

@Aaronontheweb
Copy link
Member

Cherry-pick of #7933 from dev to v1.5

Summary

Backports semantic logging support to v1.5 release branch.

This adds native semantic logging support with property extraction, allowing structured logging with named placeholders (e.g., Log.Info("User {UserId} logged in", userId)).

Original PR: #7933

…kkadotnet#7933)

* feat: Add native semantic logging support to Akka.NET core

Implements semantic/structured logging with support for both positional ({0})
and named ({PropertyName}) message templates, enabling structured property
extraction for external logging frameworks.

Key Features:
- MessageTemplateParser with ThreadStatic LRU cache for template parsing
- LogMessage enhanced with PropertyNames and GetProperties() APIs
- SemanticLogMessageFormatter for Serilog-style template formatting
- LogEventExtensions helper methods for easy property extraction
- StandardOutLogger updated to display semantic properties
- Zero new dependencies - pure BCL implementation
- Full backward compatibility maintained

Performance Optimizations:
- ThreadStatic caching avoids lock contention
- Lazy property evaluation (zero cost if not used)
- FrozenDictionary on .NET 8+ for optimal read performance
- LRU eviction prevents unbounded cache growth

Testing:
- 25 new unit tests covering template parsing, property extraction, and formatting
- All 79 existing logger tests pass (full backward compatibility)
- Tests validate positional templates, named templates, edge cases, and caching

This enables external logger plugins (Serilog, NLog, MEL) to easily extract
structured properties using logEvent.TryGetProperties() for integration with
their native structured logging capabilities.

Addresses akkadotnet#7932

* perf: optimize semantic logging memory allocations (75% reduction)

Implemented Priority 1 performance optimizations to reduce GC pressure
in semantic logging operations.

Changes:
- LogMessage.GetProperties(): Avoid ToArray() when Parameters() returns
  IReadOnlyList<object> (LogValues<T> structs), saving ~200-300 bytes
- SemanticLogMessageFormatter.Format(): Check args type before conversion,
  use IReadOnlyList directly for named templates, only convert to array
  when required by string.Format(), saving ~500-800 bytes
- SemanticLoggingBenchmarks: Add comprehensive benchmark suite (34 benchmarks)
  and fix GlobalSetup to include GetProperties benchmarks

Performance Results:
- Full E2E pipeline: 1592B → 400B (75% reduction) 🎯
- Format 3 params: 1248B → 680B (45% reduction)
- GetProperties access: 526ns → 1.7ns (99.7% faster)
- Template cache hits: 70ns → 47ns (33% faster)
- E2E semantic logging: 1.34μs → 284ns (79% faster)

All 79 unit tests passing. Benchmarks confirm optimizations maintain
correctness while achieving target allocation reductions.

Addresses akkadotnet#7932

* Enable SemanticLogMessageFormatter as default logger formatter

Changed the default logger formatter from DefaultLogMessageFormatter to SemanticLogMessageFormatter to enable semantic logging support by default. This allows both positional {0} and named {PropertyName} templates to work out of the box.

Changes:
- Updated akka.conf to use SemanticLogMessageFormatter as default
- Added special case handling in Settings.cs for SemanticLogMessageFormatter singleton instance

All 62 existing logger tests pass, confirming backward compatibility with positional templates while enabling new semantic logging capabilities.

* feat: Add EventFilter support for semantic logging templates

Enables EventFilter to match against semantic logging templates in unit tests, resolving the core issue from GitHub akkadotnet#7932 where EventFilter.Info("BetId:{BetId}") would fail to match log messages using named property syntax.

Changes:
- Modified EventFilterBase.InternalDoMatch to check LogMessage.Format template before falling back to formatted output
- Allows matching against both template patterns ("{UserId}") and formatted values ("12345")
- Added comprehensive tests for EventFilter with semantic templates (exact match, contains, starts with)
- Removed FormatException catching for positional templates to maintain backward compatibility with DefaultLogMessageFormatter

All 66 logger tests pass, including 4 new EventFilter semantic logging tests and existing backward compatibility tests.

* test: Add semantic logging integration tests for log filtering

Added 8 comprehensive tests verifying that log filtering works correctly with semantic logging templates. Tests cover:

- Filtering by formatted message content with named properties
- Filtering by property values (e.g., {AlertLevel} = "CRITICAL")
- Multiple properties in single log message
- Positional templates with filtering (backward compatibility)
- Source filtering combined with semantic logging
- Format specifiers in templates (e.g., {Amount:N2})
- Messages that should pass through filters

All 25 log filter tests pass (17 existing + 8 new), confirming semantic logging integrates seamlessly with the log filtering system introduced in v1.5.21.

* fix: Update ConfigurationSpec to expect SemanticLogMessageFormatter as default

Updated the configuration validation test to expect SemanticLogMessageFormatter instead of DefaultLogMessageFormatter as the default logger formatter, matching the change made in commit f9a2d2c.

All 4 configuration tests pass.

* fix: enable nullable reference types in LogEventExtensions

- Added #nullable enable directive
- Marked 'properties' out parameter as nullable in TryGetProperties
- Ensures proper null safety for the semantic logging API

* test: Add semantic logging edge cases verification test

- Added ShouldHandleSemanticLogEdgeCases test to DefaultLogFormatSpec
- Tests named properties, positional properties, mixed types, null values,
  special characters, booleans, dates, and formatting alignment
- Reuses existing sanitization methods from DefaultLogFormatSpec
- Verifies semantic logging formatter output for various edge cases

* Update API Approval list

* Add new edge case unit tests (failing)

* docs: Add Message Templates spec reference to SemanticLogMessageFormatter

- Added link to https://messagetemplates.org/ specification
- Documented supported syntax (named/positional properties, format specifiers, alignment, escaped braces)
- Documented unsupported syntax (destructuring operators, empty property names)

* fix: Correct escaped brace handling in semantic logging per Message Templates spec

Parser fixes:
- Removed incorrect }} check after placeholder closing brace
- Parser now correctly extracts {UserId} from "{UserId}}" and "{{{UserId}}}"

Formatter fixes:
- Rewrote FormatNamedTemplate to handle }} in literal text correctly
- Added UnescapeBraces helper for templates with no placeholders
- "Use {{ and }}" now correctly produces "Use { and }"

Test updates:
- Updated {:N2} test to document as invalid per Message Templates spec
- Invalid templates have "garbage in, garbage out" behavior (not crashing)

Fixes edge cases reported in commit 1c58a6b.
All 34 semantic logging tests now pass.

* fix: Use culture-independent format specifiers in verify test

The ShouldHandleSemanticLogEdgeCases verify test was failing on CI due
to locale differences:
- {Amount:C} produces $123.45 on US locale but ¤123.45 on invariant
- DateTime.ToString() produces different formats per locale

Changed to culture-independent formats:
- Use ${Amount:F2} (literal $ + fixed-point number) instead of {Amount:C}
- Use {JoinDate:yyyy-MM-dd} (ISO 8601) for dates

* test: Add escaped brace benchmarks and .NET Framework verified file

- Added benchmark category for escaped brace handling to track
  performance of edge case fixes
- Added .Net.verified.txt baseline for .NET Framework 4.8 CI runs

* Add unit tests

* fix: Implement alignment specifiers and null ToString() handling in SemanticLogMessageFormatter

- Add support for alignment specifiers in named templates per Message Templates spec
  - Parse {Name,alignment:format} syntax correctly
  - Apply PadLeft() for positive alignment (right-align)
  - Apply PadRight() for negative alignment (left-align)

- Fix null handling when ToString() returns null
  - Check ToString() result before attempting format operations
  - Return "null" string instead of empty string for null ToString() results
  - Handles both plain and formatted property cases

- Fix test bug: missing '>' character in alignment test format string

These changes ensure the semantic logging formatter correctly implements the
Message Templates specification for alignment and handles defensive edge cases.

---------

Co-authored-by: Gregorius Soedharmo <arkatufus@yahoo.com>
@Aaronontheweb Aaronontheweb added this to the 1.5.57 milestone Dec 2, 2025
@Aaronontheweb Aaronontheweb enabled auto-merge (squash) December 2, 2025 04:02
@Aaronontheweb Aaronontheweb merged commit 0f23948 into akkadotnet:v1.5 Dec 2, 2025
6 of 11 checks passed
@Aaronontheweb Aaronontheweb deleted the backport/semantic-logging-v1.5 branch December 2, 2025 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant