Skip to content

Commit 38883c9

Browse files
committed
docs(nlog): update README for new architecture
1 parent ca6c3e9 commit 38883c9

File tree

1 file changed

+77
-38
lines changed
  • src/OpenTelemetry.AutoInstrumentation/Instrumentations/NLog

1 file changed

+77
-38
lines changed

src/OpenTelemetry.AutoInstrumentation/Instrumentations/NLog/README.md

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,25 @@ This directory contains the NLog instrumentation for OpenTelemetry .NET Auto-Ins
55
## Overview
66

77
The NLog instrumentation offers automatic integration through:
8-
1. **Bytecode Interception**: Automatically intercepts `NLog.Logger.Log` calls via bytecode instrumentation
8+
1. **Bytecode Interception**: Automatically intercepts NLog's internal `WriteToTargets` methods via bytecode instrumentation
99
2. **Duck Typing Integration**: Uses duck typing to avoid direct NLog assembly references
1010
3. **Log Event Bridging**: Converting NLog log events to OpenTelemetry log records (when bridge is enabled)
1111
4. **Structured Logging Support**: Leveraging NLog's layout abilities for enrichment
1212
5. **Trace Context Injection**: Automatically injects trace context into NLog properties for all targets
13-
6. **Custom Properties**: Forwarding custom properties while filtering internal NLog properties
13+
6. **GlobalDiagnosticsContext Support**: Captures properties from NLog's GlobalDiagnosticsContext
14+
7. **Custom Properties**: Forwarding custom properties while filtering internal NLog properties
1415

1516
**Note**: No NLog configuration changes are required. The instrumentation works exclusively through bytecode interception and relies on OpenTelemetry environment variables for configuration.
1617

1718
## Architecture
1819

1920
### Bytecode Interception Path
2021
```
21-
NLog Logger.Log() Call
22+
NLog Logger.Info/Debug/Warn/Error/etc. Call
2223
23-
LoggerIntegration (CallTarget - Bytecode Interception)
24+
NLog Internal WriteToTargets/WriteLogEventToTargets
25+
26+
WriteToTargetsIntegration / WriteLogEventToTargetsIntegration (CallTarget - Bytecode Interception)
2427
├─ ALWAYS: Inject trace context into NLog properties
2528
│ (Available to ALL NLog targets: file, console, database, etc.)
2629
@@ -33,25 +36,31 @@ LoggerIntegration (CallTarget - Bytecode Interception)
3336
OTLP Exporters
3437
```
3538

36-
The instrumentation intercepts `NLog.Logger.Log` method calls at the bytecode level, allowing it to:
37-
1. **Always inject trace context** into NLog's LogEventInfo properties (regardless of bridge status)
38-
2. **Optionally forward logs** to OpenTelemetry when the bridge is enabled
39+
The instrumentation intercepts NLog's internal `WriteToTargets` (NLog 5.x) and `WriteLogEventToTargets` (NLog 6.x) methods at the bytecode level. This ensures ALL log events are captured, including those from convenience methods like `log.Info()`, `log.Debug()`, etc.
3940

4041
## Components
4142

4243
### Core Components
4344

44-
#### Auto-Instrumentation Components
45-
- **`ILoggingEvent.cs`**: Duck typing interface for NLog's LogEventInfo
46-
- **`OpenTelemetryNLogConverter.cs`**: Internal converter that transforms NLog events to OpenTelemetry log records
47-
- **`OpenTelemetryLogHelpers.cs`**: Helper for creating OpenTelemetry log records via expression trees
45+
#### Duck Types (`ILoggingEvent.cs`)
46+
- **`ILoggingEvent`**: Duck typing struct for NLog's LogEventInfo
47+
- **`LoggingLevel`**: Duck typing struct for NLog's LogLevel
48+
- **`IMessageTemplateParameters`**: Interface for structured logging parameters
4849

49-
### Integration
50+
#### Bridge Components (`Bridge/`)
51+
- **`OpenTelemetryNLogConverter.cs`**: Converts NLog events to OpenTelemetry log records
52+
- **`OpenTelemetryLogHelpers.cs`**: Helper for creating OpenTelemetry log records via expression trees
5053

51-
- **`LoggerIntegration.cs`**: CallTarget integration that intercepts `NLog.Logger.Log` via bytecode instrumentation to capture log events
54+
### Trace Context Injection (`TraceContextInjection/`)
5255

53-
### Trace Context
56+
#### Integrations
57+
- **`WriteToTargetsIntegration.cs`**: For NLog 5.3.0+ (uses `ITargetWithFilterChain` interface)
58+
- **`WriteToTargetsLegacyIntegration.cs`**: For NLog 5.0.0-5.2.x (uses `TargetWithFilterChain` class)
59+
- **`WriteLogEventToTargetsIntegration.cs`**: For NLog 6.x (method renamed to `WriteLogEventToTargets`)
60+
- **`NLogIntegrationHelper.cs`**: Shared helper with common trace context injection and bridge logic
5461

62+
#### Supporting Types
63+
- **`ILogEventInfoProperties.cs`**: Duck-typed interface for accessing LogEventInfo.Properties
5564
- **`LogsTraceContextInjectionConstants.cs`**: Constants for trace context property names
5665

5766
## Configuration
@@ -84,6 +93,7 @@ The instrumentation automatically:
8493
- Uses formatted message if available, otherwise raw message (when bridge enabled)
8594
- Includes event parameters when present (when bridge enabled)
8695
- Captures trace context from `Activity.Current`
96+
- Captures properties from `GlobalDiagnosticsContext`
8797
- Forwards custom properties while filtering internal NLog properties (when bridge enabled)
8898

8999
#### Trace Context Injection
@@ -98,15 +108,17 @@ Trace context is **always injected** into NLog's LogEventInfo properties, regard
98108
The following properties are injected when an active `Activity` exists:
99109
- `TraceId`: The W3C trace ID
100110
- `SpanId`: The W3C span ID
101-
- `TraceFlags`: The W3C trace flags
111+
- `TraceFlags`: The W3C trace flags ("01" if recorded, "00" otherwise)
102112

103113
#### OpenTelemetry Bridge
104114

105115
When `OTEL_DOTNET_AUTO_LOGS_ENABLE_NLOG_BRIDGE=true`, log events are additionally forwarded to OpenTelemetry's logging infrastructure for export via OTLP or other configured exporters.
106116

107117
## Supported Versions
108118

109-
- **NLog**: 5.0.0+ (required for Layout<T> typed layout support and .NET build-trimming)
119+
- **NLog 5.0.0 - 5.2.x**: Uses `WriteToTargetsLegacyIntegration` (TargetWithFilterChain)
120+
- **NLog 5.3.0+**: Uses `WriteToTargetsIntegration` (ITargetWithFilterChain)
121+
- **NLog 6.x**: Uses `WriteLogEventToTargetsIntegration` (WriteLogEventToTargets method)
110122
- **.NET Framework**: 4.6.2+
111123
- **.NET**: 8.0, 9.0
112124

@@ -122,51 +134,70 @@ NLog levels are mapped to OpenTelemetry log record severity levels:
122134
| Warn | 3 | Warn | 13 |
123135
| Error | 4 | Error | 17 |
124136
| Fatal | 5 | Fatal | 21 |
125-
| Off | 6 | Trace | 1 |
137+
| Off | 6 | (skipped) | - |
126138

127139
## Duck Typing
128140

129141
The instrumentation uses duck typing to interact with NLog without requiring direct references:
130142

131-
- **`ILoggingEvent`**: Maps to `NLog.LogEventInfo`
132-
- **`LoggingLevel`**: Maps to `NLog.LogLevel`
143+
- **`ILoggingEvent`**: Maps to `NLog.LogEventInfo` (using `[DuckCopy]` struct)
144+
- **`LoggingLevel`**: Maps to `NLog.LogLevel` (using `[DuckCopy]` struct)
145+
- **`ILogEventInfoProperties`**: Maps to LogEventInfo.Properties for trace context injection
133146
- **`IMessageTemplateParameters`**: Maps to structured logging parameters
134147

135-
## Property Filtering
148+
## Property Handling
149+
150+
### Captured Properties
151+
- All custom properties from `LogEventInfo.Properties`
152+
- All properties from `GlobalDiagnosticsContext`
153+
- Message template arguments (indexed as "0", "1", "2", etc.)
136154

155+
### Filtered Properties
137156
The following properties are filtered out when forwarding to OpenTelemetry:
138157
- Properties starting with `NLog.`
139158
- Properties starting with `nlog:`
140159
- OpenTelemetry trace context properties (`SpanId`, `TraceId`, `TraceFlags`)
141160

161+
### Exception Handling
162+
Exceptions are recorded using OpenTelemetry's `RecordException` method, which adds:
163+
- `exception.type`: The exception type name
164+
- `exception.message`: The exception message
165+
- `exception.stacktrace`: The full stack trace
166+
142167
## Performance Considerations
143168

144-
- **Logger Caching**: OpenTelemetry loggers are cached to avoid recreation overhead
169+
- **Logger Caching**: OpenTelemetry loggers are cached (up to 100 entries) to avoid recreation overhead
145170
- **Lazy Initialization**: Components are initialized only when needed
146171
- **Minimal Overhead**: Bytecode interception adds minimal overhead to logging calls
172+
- **Reflection Caching**: GlobalDiagnosticsContext access is cached via delegates
147173

148174
## Error Handling
149175

150176
- **Graceful Degradation**: If OpenTelemetry components fail to initialize, logging continues normally
151177
- **Property Safety**: Property extraction is wrapped in try-catch to handle potential NLog configuration issues
152-
- **Instrumentation Conflicts**: Automatically disables when other logging bridges are active
178+
- **Instrumentation Conflicts**: Automatically disables NLog bridge when ILogger bridge is active to prevent duplicate logs
153179

154180
## Testing
155181

156-
Tests are located in `test/OpenTelemetry.AutoInstrumentation.Tests/NLogTests.cs` and cover:
182+
### Unit Tests
183+
Tests are located in `test/OpenTelemetry.AutoInstrumentation.Tests/` and cover:
157184
- Level mapping verification
158185
- Edge case handling (invalid levels, off level)
159-
- Custom level support
160-
- Range-based mapping logic
161186

162-
## Integration Testing
187+
### Integration Tests
188+
Tests are located in `test/IntegrationTests/NLogBridgeTests.cs` and cover:
189+
- Direct NLog bridge logging (`SubmitLogs_ThroughNLogBridge_WhenNLogIsUsedDirectlyForLogging`)
190+
- ILogger bridge with NLog provider (`SubmitLogs_ThroughILoggerBridge_WhenNLogIsUsedAsILoggerProviderForLogging`)
191+
- Duplicate prevention (`SubmitLogs_ThroughILoggerBridge_WhenNLogIsUsedAsILoggerProviderForLogging_WithoutDuplicates`)
192+
- Trace context injection (`TraceContext_IsInjectedIntoCurrentNLogLogsDestination`)
163193

194+
### Test Application
164195
A complete test application is available at `test/test-applications/integrations/TestApplication.NLogBridge/` that demonstrates:
165196
- Direct NLog usage
166-
- Microsoft.Extensions.Logging integration via custom provider
197+
- Microsoft.Extensions.Logging integration via NLogLoggerProvider
167198
- Structured logging scenarios
168199
- Exception logging
169-
- Custom properties
200+
- GlobalDiagnosticsContext properties
170201
- Trace context propagation
171202

172203
## Troubleshooting
@@ -175,27 +206,35 @@ A complete test application is available at `test/test-applications/integrations
175206

176207
1. **Bridge Not Working**
177208
- Verify `OTEL_DOTNET_AUTO_LOGS_ENABLE_NLOG_BRIDGE=true`
178-
- Check that NLog version is supported
209+
- Check that NLog version is 5.0.0 or higher
179210
- Ensure auto-instrumentation is properly loaded
180211

181-
2. **Missing Properties**
212+
2. **Missing Trace Context**
213+
- Verify `OTEL_DOTNET_AUTO_TRACES_ENABLED=true`
214+
- Ensure an `Activity` is active when logging
215+
- Check NLog layout includes `${event-properties:TraceId}` etc.
216+
217+
3. **Missing Properties**
182218
- Check NLog configuration for property capture
183219
- Verify properties don't start with filtered prefixes
220+
- Ensure GlobalDiagnosticsContext.Set() is called before logging
184221

185-
3. **Performance Impact**
186-
- Monitor logger cache efficiency
187-
- Consider adjusting cache size if many dynamic logger names are used
222+
4. **Duplicate Logs**
223+
- If using NLog as ILogger provider, logs go through ILogger bridge
224+
- NLog bridge automatically disables when ILogger bridge is active
188225

189226
### Debug Information
190227

191-
Enable debug logging to see:
192-
- Bytecode interception success/failure
193-
- Logger creation and caching
194-
- Property filtering decisions
228+
Enable OpenTelemetry auto-instrumentation logging:
229+
```bash
230+
export OTEL_DOTNET_AUTO_LOG_LEVEL=debug
231+
export OTEL_DOTNET_AUTO_LOG_DIRECTORY=/path/to/logs
232+
```
195233

196234
## Implementation Notes
197235

198236
- Uses reflection to access internal OpenTelemetry logging APIs (until public APIs are available)
199237
- Builds expression trees dynamically for efficient log record creation
200238
- Follows the same patterns as Log4Net instrumentation for consistency
201-
- Designed to be thread-safe and performant in high-throughput scenarios
239+
- Designed to be thread-safe and performant in high-throughput scenarios
240+
- Three separate integrations handle NLog version differences (5.0-5.2, 5.3+, 6.x)

0 commit comments

Comments
 (0)