Commit 3b51f6a
Add NLog instrumentation for OpenTelemetry .NET Auto-Instrumentation (#4371)
* Add NLog instrumentation for OpenTelemetry .NET Auto-Instrumentation
This commit implements a comprehensive NLog instrumentation that automatically
bridges NLog logging events to OpenTelemetry without requiring code changes.
Features:
- Automatic target injection into NLog's target collection
- Complete log event bridging with level mapping
- Structured logging support with message templates
- Trace context integration
- Custom properties forwarding with filtering
- Comprehensive test coverage
- End-to-end test application
- Extensive documentation
The implementation follows the same patterns as the existing Log4Net
instrumentation and supports NLog versions 4.0.0 through 6.*.*.
Configuration:
- OTEL_DOTNET_AUTO_LOGS_ENABLED=true
- OTEL_DOTNET_AUTO_LOGS_ENABLE_NLOG_BRIDGE=true
- OTEL_DOTNET_AUTO_LOGS_INCLUDE_FORMATTED_MESSAGE=true (optional)
Files added:
- src/OpenTelemetry.AutoInstrumentation/Instrumentations/NLog/
- test/OpenTelemetry.AutoInstrumentation.Tests/NLogTests.cs
- test/test-applications/integrations/TestApplication.NLog/
Files modified:
- Configuration classes to support NLog bridge
- Public API files to include new integration class
* Fix: #4371 (comment)
* Fix #4371 (comment): correct integration to hook into Logger.Log method
The original integration targeted a non-existent method
'GetConfiguredNamedTargets' in recent NLog versions. Changed to
intercept the actual NLog.Logger.Log(LogEventInfo) method which
is stable across NLog 4.0+ versions.
- Renamed TargetCollectionIntegration to LoggerIntegration
- Changed from OnMethodEnd to OnMethodBegin approach
- Updated public API references
- Fixes instrumentation for all NLog versions 4.0-6.*.*
* Fix: #4371 (comment) missing nlog instrumentation definition for net462
* Test: #4371 (comment). feat(nlog): add integration tests following Log4NetBridge pattern
Rework TestApplication.NLog into TestApplication.NLogBridge with proper
integration test support. Add NLogBridgeTests.cs with complete coverage
of direct NLog usage, ILogger bridge, and trace context injection.
- Add to solution and LibraryVersionsGenerator
- Fix config to remove Windows-specific paths
- Support --api nlog and --api ILogger test modes
* feat: refactor NLog instrumentation to use standard NLog Target architecture
- Replace CallTarget interception with standard NLog.Targets.TargetWithContext
- Add OpenTelemetry.AutoInstrumentation.NLogTarget project with OpenTelemetryTarget
- Implement NLogAutoInjector for zero-config auto-injection via CallTarget
- Rename OpenTelemetryNLogTarget to OpenTelemetryNLogConverter for clarity
- Update LoggerIntegration to trigger auto-injection and set GlobalDiagnosticsContext
- Rework TestApplication.NLogBridge to follow standard integration test pattern
- Update NLogBridgeTests to match Log4NetBridgeTests structure
- Add InternalsVisibleTo for new NLog target project access
- Update documentation to reflect dual-path architecture (auto-injection + manual config)
- Remove obsolete files and clean up project structure
This refactor aligns with NLog best practices by providing both automatic
instrumentation and a standard NLog Target that can be configured via
nlog.config or programmatically, leveraging NLog's native layout and
routing capabilities.
* refactor: remove unused NLog.Extensions.Logging from TestApplication.NLogBridge
The package was not referenced in any source files and the test app implements
its own ILogger bridge for testing purposes.
* refactor: optimize NLog target for async compatibility and performance
- Replace Activity.Current with Layout-based trace context resolution
- Add HasProperties check to avoid unnecessary dictionary allocation
- Remove custom Attributes property, use base class ContextProperties
- Fix parameter vs properties logic to prevent duplication
- Standardize Layout rendering patterns with RenderLogEvent
- Improve resource management with proper null handling
Enhances AsyncWrapper compatibility while reducing memory allocations
and aligning with NLog target best practices.
* feat: add hybrid typed layout support for NLog version compatibility
Implements runtime version detection to use Layout<T> for NLog 5.3.4+
while maintaining backward compatibility with NLog 4.0.0+. Eliminates
string parsing overhead on modern versions while preserving existing API.
- Add runtime NLog version detection (5.3.4+ supports typed layouts)
- Use Layout<ActivityTraceId?> and Layout<ActivitySpanId?> internally when supported
- Fall back to string parsing for older NLog versions
- Maintain public Layout? API for full backward compatibility
- Remove unnecessary LINQ conversion in GetLogEventProperties
* feat: implement NLog v5.3.4+ typed layouts for OpenTelemetryTarget
Resolves critical and high-priority issues in OpenTelemetryTarget by implementing
modern NLog typed layouts and fixing AsyncWrapper compatibility.
## Key Improvements
### Critical Issues Fixed
- **Unused Layout Properties**: TraceIdLayout and SpanIdLayout are now properly
implemented using Layout<ActivityTraceId?> and Layout<ActivitySpanId?> with
Layout<T>.FromMethod for optimal performance
- **AsyncWrapper Compatibility**: Fixed trace context resolution in async scenarios
by using layout-based approach that captures context at log event creation time
- **Redundant Rendering**: Eliminated duplicate RenderLogEvent calls by rendering
layout only once and reusing the result
### Performance Enhancements
- Implemented NLog v5.3.4+ Layout<T>.FromMethod with static lambda expressions
- Reduced memory allocations through typed layouts (eliminates boxing/parsing)
- Optimized layout rendering to avoid redundant string operations
- Added proper type safety with strongly-typed Layout<T> properties
### Code Quality
- Removed unnecessary null-forgiving operator usage
- Added comprehensive documentation explaining AsyncWrapper compatibility approach
- Updated PublicAPI definitions to reflect new typed layout signatures
- Maintained backward compatibility while leveraging modern NLog features
## Technical Details
**Before**:
- TraceIdLayout/SpanIdLayout were unused Layout? properties
- Multiple RenderLogEvent calls caused performance overhead
- AsyncWrapper scenarios lost trace context due to Activity.Current limitations
**After**:
- Typed layouts: Layout<ActivityTraceId?> and Layout<ActivitySpanId?>
- Single layout rendering with result reuse
- Layout-based trace context resolution works across async boundaries
- Static lambda expressions:
## Testing
- ✅ All unit tests pass (31/31)
- ✅ Project builds without errors or warnings
- ✅ PublicAPI validation passes
- ✅ No linter errors
Closes: Critical and high findings in OpenTelemetryTarget code review
* fix test coverage
* fix: correct NLog bridge EmitLog method call to match OpenTelemetry SDK
- Change EmitLogRecord to EmitLog method name
- Use ref parameter types with MakeByRefType()
- Add explicit BindingFlags for method resolution
- Align with Log4Net bridge implementation
Fixes NLog bridge initialization failures due to method not found errors.
* refactor: simplify NLog target configuration to use environment variables only
- Remove all programmatic configuration properties from OpenTelemetryTarget
- Simplify OTLP exporter setup to use standard environment variables
- Use reasonable defaults for message formatting and parameter inclusion
- Update documentation to reflect environment-only configuration
- Maintain all existing functionality while eliminating assembly loading risks
* Removed NLog from AssemblyInfo
* feat: add NLog instrumentation with duck typing and NLog 6.x support
- Add NLog logs instrumentation for versions 5.0+ and 6.x
- Use duck typing for zero-config auto-injection without assembly references
- Configure via OpenTelemetry environment variables only
- Update CHANGELOG.md and config.md documentation
- Add NLog 6.0.0 to test matrix for broader version coverage
* fix CHANGELOG
* Fix build errors
* commit generated file
* Move NLog version to test folder
* cleanup solution
* add NLOG to dictionary
* typo fixes
* remove reference to NLog.Extensions.Logging
it is not used
* Update tested versions
* Minimal assembly version set to 4.0.0
There is no tests for these versions
* fix sln file
* remove reference to System.Private.Uri
seems to be redundant
* Fix compilation for tests app
* Apply suggestions from code review
* Fix issue occurring in VS
* Add missing settings test case
* remove redundant lines
* Sync implementation with available documentation
* user facing documentation
* refactor: Switch NLog integration from target injection to bytecode interception
Replace target-based approach with direct Logger.Log() interception using duck typing.
Remove OpenTelemetryTarget and NLogAutoInjector, update LoggerProvider initialization,
and add comprehensive test coverage for all log levels including Trace/Debug.
* fix: resolve kielek feedback about injecting trace context even when nlog bridge is disabled
* refactor(nlog): target WriteToTargets instead of Logger.Log
Intercept WriteToTargets/WriteLogEventToTargets to capture all log
events including convenience methods (log.Info, log.Debug, etc).
- Add WriteToTargetsIntegration for NLog 5.3.0+
- Add WriteToTargetsLegacyIntegration for NLog 5.0.0-5.2.x
- Add WriteLogEventToTargetsIntegration for NLog 6.x
- Add NLogIntegrationHelper to share logic
- Remove LoggerIntegration (only caught explicit Logger.Log calls)
* fix(nlog): correct log record attribute handling
- Use RecordException for proper exception attributes
- Remove "arg_" prefix from parameter keys
- Add GlobalDiagnosticsContext property capture
- Remove unused import
* test(nlog): update tests for new instrumentation approach
- Fix trace context injection test regex
- Expect "Information" for ILogger bridge
- Add GlobalDiagnosticsContext test property
- Add trace context to nlog.config layout
* docs(nlog): update README for new architecture
* refactor(nlog): remove GlobalDiagnosticsContext integration
GDC is for static application-wide properties set at startup.
OpenTelemetry has its own enrichment for service metadata.
* fix: remove duplicate package versions in Directory.Packages.props
* fix(nlog): support .NET Framework expression tree limitations
Assign log record and attributes to local variables before passing
to EmitLog method with ref parameters. .NET Framework's expression
tree compiler doesn't support TryExpression nested in ref parameter
method calls.
* fix: removed duplicates from the unshipped api
* Fix changelog place
* generated files
* Bump NLog to 6.0.6
* Comments why we need more library versions in the coverage
* Fix NLog trace context injection test
* Add longdate to spellcheck dictionary
* Fix regex to handle 12-hour time format
* Fix duplicate NET472 in NetFramework array
* Fix NLog trace context test regex for macOS locale
macOS uses narrow no-break space (U+202F) between time and AM/PM,
not regular ASCII space. Use \s* to match any Unicode whitespace.
* File based configuration
* cleanup test application
* Modern array initialization
* Add NLog integrations for wrapperType overload
Cover Logger.Log(Type wrapperType, LogEventInfo logEvent) API:
- WriteLogEventToTargetsWithWrapperTypeIntegration for NLog 6.x
- WriteToTargetsWithWrapperTypeIntegration for NLog 5.3.0+
- WriteToTargetsWithWrapperTypeLegacyIntegration for NLog 5.0.0-5.2.x
* Remove unused IMessageTemplateParameters interface
* Use official NLog.Extensions.Logging package
Replace custom NLogLoggerProvider with official NLog.Extensions.Logging
package for ILogger integration testing.
* Fix ClrNames.Type reference and SA1518 error
- Use "System.Type" string instead of non-existent ClrNames.Type
- Fix trailing newline in ILoggingEvent.cs
* Consolidate NLog integrations from 6 files to 2
Use multiple [InstrumentMethod] attributes per class:
- NLogWriteToTargetsIntegration: 2-param methods (3 attributes)
- NLogWriteToTargetsWithWrapperTypeIntegration: 3-param methods (3 attributes)
* generated file
* Centos SHA update
* lint check
* Add test coverage for Logger.Log(Type wrapperType, LogEventInfo) overload
- Add LogWithWrapperType method to test application to exercise 3-parameter
WriteToTargets/WriteLogEventToTargets instrumentation
- Add test expectation for wrapperType log message in NLogBridgeTests
- Add NLog.Extensions.Logging to central package management
- Update AssertStandardOutputExpectations to verify wrapperType message
* Fix build after merge with main
* Improve managing tests dependencies
* Fig merge main issue
---------
Co-authored-by: Piotr Kiełkowicz <pkiekowicz@splunk.com>
Co-authored-by: Mateusz Łach <mateusza@splunk.com>1 parent 5c16060 commit 3b51f6a
File tree
40 files changed
+1976
-13
lines changed- .cspell
- build
- docs
- src/OpenTelemetry.AutoInstrumentation
- .publicApi
- net462
- net8.0
- Configurations
- FileBasedConfiguration
- Generated
- net462/SourceGenerators/SourceGenerators.InstrumentationDefinitionsGenerator
- net8.0/SourceGenerators/SourceGenerators.InstrumentationDefinitionsGenerator
- Instrumentations/NLog
- Bridge
- TraceContextInjection
- Integrations
- test
- IntegrationTests
- OpenTelemetry.AutoInstrumentation.Tests
- Configurations
- FileBased
- test-applications/integrations/TestApplication.NLogBridge
- tools/LibraryVersionsGenerator
- Models
40 files changed
+1976
-13
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
| 45 | + | |
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
14 | 17 | | |
15 | 18 | | |
16 | 19 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
246 | 246 | | |
247 | 247 | | |
248 | 248 | | |
| 249 | + | |
| 250 | + | |
249 | 251 | | |
250 | 252 | | |
251 | 253 | | |
| |||
1538 | 1540 | | |
1539 | 1541 | | |
1540 | 1542 | | |
| 1543 | + | |
| 1544 | + | |
| 1545 | + | |
| 1546 | + | |
| 1547 | + | |
| 1548 | + | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
1541 | 1559 | | |
1542 | 1560 | | |
1543 | 1561 | | |
| |||
1692 | 1710 | | |
1693 | 1711 | | |
1694 | 1712 | | |
| 1713 | + | |
1695 | 1714 | | |
1696 | 1715 | | |
1697 | 1716 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
71 | 80 | | |
72 | 81 | | |
73 | 82 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
| 209 | + | |
209 | 210 | | |
210 | 211 | | |
211 | 212 | | |
| |||
215 | 216 | | |
216 | 217 | | |
217 | 218 | | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
218 | 222 | | |
219 | 223 | | |
220 | 224 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
526 | 526 | | |
527 | 527 | | |
528 | 528 | | |
| 529 | + | |
529 | 530 | | |
530 | 531 | | |
531 | 532 | | |
| |||
567 | 568 | | |
568 | 569 | | |
569 | 570 | | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
570 | 575 | | |
571 | 576 | | |
572 | 577 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| 2 | + | |
0 commit comments