Commit b85078f
feat: Add native semantic logging support with property extraction (#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 #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 #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 #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 <[email protected]>1 parent 747abfc commit b85078f
File tree
17 files changed
+2298
-9
lines changed- src
- benchmark/Akka.Benchmarks/Logging
- core
- Akka.API.Tests
- verify
- Akka.TestKit/EventFilter/Internal
- Akka.Tests
- Configuration
- Loggers
- Akka
- Actor
- Configuration
- Event
17 files changed
+2298
-9
lines changedLines changed: 534 additions & 0 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
| 53 | + | |
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
118 | 170 | | |
119 | 171 | | |
120 | 172 | | |
| |||
Lines changed: 19 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3472 | 3472 | | |
3473 | 3473 | | |
3474 | 3474 | | |
| 3475 | + | |
| 3476 | + | |
| 3477 | + | |
| 3478 | + | |
| 3479 | + | |
| 3480 | + | |
| 3481 | + | |
| 3482 | + | |
| 3483 | + | |
| 3484 | + | |
| 3485 | + | |
3475 | 3486 | | |
3476 | 3487 | | |
3477 | 3488 | | |
| |||
3531 | 3542 | | |
3532 | 3543 | | |
3533 | 3544 | | |
| 3545 | + | |
| 3546 | + | |
3534 | 3547 | | |
3535 | 3548 | | |
3536 | 3549 | | |
| |||
3708 | 3721 | | |
3709 | 3722 | | |
3710 | 3723 | | |
| 3724 | + | |
| 3725 | + | |
| 3726 | + | |
| 3727 | + | |
| 3728 | + | |
| 3729 | + | |
3711 | 3730 | | |
3712 | 3731 | | |
3713 | 3732 | | |
| |||
Lines changed: 19 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3483 | 3483 | | |
3484 | 3484 | | |
3485 | 3485 | | |
| 3486 | + | |
| 3487 | + | |
| 3488 | + | |
| 3489 | + | |
| 3490 | + | |
| 3491 | + | |
| 3492 | + | |
| 3493 | + | |
| 3494 | + | |
| 3495 | + | |
| 3496 | + | |
3486 | 3497 | | |
3487 | 3498 | | |
3488 | 3499 | | |
| |||
3542 | 3553 | | |
3543 | 3554 | | |
3544 | 3555 | | |
| 3556 | + | |
| 3557 | + | |
3545 | 3558 | | |
3546 | 3559 | | |
3547 | 3560 | | |
| |||
3717 | 3730 | | |
3718 | 3731 | | |
3719 | 3732 | | |
| 3733 | + | |
| 3734 | + | |
| 3735 | + | |
| 3736 | + | |
| 3737 | + | |
| 3738 | + | |
3720 | 3739 | | |
3721 | 3740 | | |
3722 | 3741 | | |
| |||
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 19 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
90 | 108 | | |
91 | | - | |
| 109 | + | |
92 | 110 | | |
93 | 111 | | |
94 | 112 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
| 53 | + | |
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| |||
0 commit comments