Skip to content

Commit 3b51f6a

Browse files
danifitzKieleklachmatt
authored
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

40 files changed

+1976
-13
lines changed

.cspell/other.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ILOGGER
2929
inetsrv
3030
JIT
3131
LINQ
32+
longdate
3233
MASSTRANSIT
3334
metricsexporter
3435
mkdir
@@ -41,6 +42,7 @@ mycompanymyproductmylibrary
4142
MYSQLCONNECTOR
4243
MYSQLDATA
4344
NETRUNTIME
45+
NLOG
4446
Npgsql
4547
NSERVICEBUS
4648
omnisharp

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ This component adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
1111

1212
- Add `-NoReset` switch parameter to skip service restart in PowerShell
1313
registration functions.
14+
- Support for [`NLog`](https://www.nuget.org/packages/NLog/)
15+
logs instrumentation for versions `5.*` and `6.*` on .NET using duck typing
16+
for zero-config auto-injection.
1417

1518
### Changed
1619

OpenTelemetry.AutoInstrumentation.sln

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SdkVersionAnalyzer", "tools
246246
EndProject
247247
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.Log4NetBridge", "test\test-applications\integrations\TestApplication.Log4NetBridge\TestApplication.Log4NetBridge.csproj", "{926B7C03-42C2-4192-94A7-CD0B1C693279}"
248248
EndProject
249+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.NLogBridge", "test\test-applications\integrations\TestApplication.NLogBridge\TestApplication.NLogBridge.csproj", "{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}"
250+
EndProject
249251
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.SelectiveSampler", "test\test-applications\integrations\TestApplication.SelectiveSampler\TestApplication.SelectiveSampler.csproj", "{FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}"
250252
EndProject
251253
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.ProfilerSpanStoppageHandling", "test\test-applications\integrations\TestApplication.ProfilerSpanStoppageHandling\TestApplication.ProfilerSpanStoppageHandling.csproj", "{665280EB-F428-4C04-A293-33228C73BF8A}"
@@ -1538,6 +1540,22 @@ Global
15381540
{926B7C03-42C2-4192-94A7-CD0B1C693279}.Release|x64.Build.0 = Release|x64
15391541
{926B7C03-42C2-4192-94A7-CD0B1C693279}.Release|x86.ActiveCfg = Release|x86
15401542
{926B7C03-42C2-4192-94A7-CD0B1C693279}.Release|x86.Build.0 = Release|x86
1543+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|Any CPU.ActiveCfg = Debug|x64
1544+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|Any CPU.Build.0 = Debug|x64
1545+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|ARM64.ActiveCfg = Debug|x64
1546+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|ARM64.Build.0 = Debug|x64
1547+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x64.ActiveCfg = Debug|x64
1548+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x64.Build.0 = Debug|x64
1549+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x86.ActiveCfg = Debug|x86
1550+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Debug|x86.Build.0 = Debug|x86
1551+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|Any CPU.ActiveCfg = Release|x64
1552+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|Any CPU.Build.0 = Release|x64
1553+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|ARM64.ActiveCfg = Release|x64
1554+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|ARM64.Build.0 = Release|x64
1555+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x64.ActiveCfg = Release|x64
1556+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x64.Build.0 = Release|x64
1557+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x86.ActiveCfg = Release|x86
1558+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D}.Release|x86.Build.0 = Release|x86
15411559
{FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}.Debug|Any CPU.ActiveCfg = Debug|x64
15421560
{FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}.Debug|Any CPU.Build.0 = Debug|x64
15431561
{FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB}.Debug|ARM64.ActiveCfg = Debug|x64
@@ -1692,6 +1710,7 @@ Global
16921710
{AA3E0C5C-A4E2-46AB-BD18-2D30D3ABF692} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
16931711
{C75FA076-D460-414B-97F7-6F8D0E85AE74} = {00F4C92D-6652-4BD8-A334-B35D3E711BE6}
16941712
{926B7C03-42C2-4192-94A7-CD0B1C693279} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
1713+
{A7B8C9D0-1E2F-3A4B-5C6D-7E8F9A0B1C2D} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
16951714
{FD1A1ABD-6A48-4E94-B5F7-2081AFCD1BBB} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
16961715
{665280EB-F428-4C04-A293-33228C73BF8A} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
16971716
{500BF40F-EECB-4F6A-377B-EDBDFFDE09BE} = {E409ADD3-9574-465C-AB09-4324D205CC7C}

build/LibraryVersions.g.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ public static partial class LibraryVersion
6868
new("3.2.0"),
6969
]
7070
},
71+
{
72+
"TestApplication.NLogBridge",
73+
[
74+
new("5.0.0", additionalMetaData: new() { { "NLogExtensionsLogging", "5.0.0" } }),
75+
new("5.3.4", additionalMetaData: new() { { "NLogExtensionsLogging", "5.3.15" } }),
76+
new("6.0.0", additionalMetaData: new() { { "NLogExtensionsLogging", "6.0.0" } }),
77+
new("6.0.6", additionalMetaData: new() { { "NLogExtensionsLogging", "6.1.0" } }),
78+
]
79+
},
7180
{
7281
"TestApplication.MassTransit",
7382
[

build/TargetFramework.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class TargetFramework : Enumeration
1919

2020
// should be in version order
2121
public static readonly TargetFramework[] NetFramework = {
22-
NET462, NET47, NET471, NET472, NET472
22+
NET462, NET47, NET471, NET472
2323
};
2424

2525
public static implicit operator string(TargetFramework framework)

docs/config.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ due to lack of stable semantic convention.
206206
|-----------|---------------------------------------------------------------------------------------------------------------------------------|--------------------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
207207
| `ILOGGER` | [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging) **Not supported on .NET Framework** | ≥8.0.0 | bytecode or source \[1\] | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
208208
| `LOG4NET` | [log4net](https://www.nuget.org/packages/log4net) \[2\] | ≥2.0.13 && < 4.0.0 | bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
209+
| `NLOG` | [NLog](https://www.nuget.org/packages/NLog) \[3\] | ≥5.0.0 && < 7.0.0 | bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
209210

210211
\[1\]: For ASP.NET Core applications, the `LoggingBuilder` instrumentation
211212
can be enabled without using the .NET CLR Profiler by setting
@@ -215,6 +216,9 @@ the `ASPNETCORE_HOSTINGSTARTUPASSEMBLIES` environment variable to
215216
\[2\]: Instrumentation provides both [trace context injection](./log-trace-correlation.md#log4net-trace-context-injection)
216217
and [logs bridge](./log4net-bridge.md).
217218

219+
\[3\]: Instrumentation provides both [trace context injection](./log-trace-correlation.md#log4net-trace-context-injection)
220+
and [logs bridge](./nlog-bridge.md).
221+
218222
### Instrumentation options
219223

220224
| Environment variable | Description | Default value | Status |

docs/file-based-configuration.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ instrumentation/development:
526526
logs:
527527
ilogger: # Microsoft.Extensions.Logging
528528
log4net: # Log4Net
529+
nlog: # NLog
529530
```
530531

531532
## Instrumentation options
@@ -567,6 +568,10 @@ instrumentation/development:
567568
# Logs bridge is disabled by default
568569
# More info about log4net bridge can be found at https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/blob/main/docs/log4net-bridge.md
569570
bridge_enabled: true
571+
nlog:
572+
# Logs bridge is disabled by default
573+
# More info about NLog bridge can be found at https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/blob/main/docs/nlog-bridge.md
574+
bridge_enabled: true
570575
```
571576

572577
## Instrumentation Additional Sources

docs/log-trace-correlation.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,27 @@ Following properties are set by default on the collection of logging event's pro
6262

6363
This allows for trace context to be logged into currently configured log destination,
6464
e.g. a file. In order to use them, pattern needs to be updated.
65+
66+
### `NLog`
67+
68+
See [`nlog-bridge`](./nlog-bridge.md).
69+
70+
## `NLog` trace context injection
71+
72+
> [!IMPORTANT]
73+
> NLog trace context injection is an experimental feature.
74+
75+
The `NLog` trace context injection is enabled by default.
76+
It can be disabled by setting
77+
`OTEL_DOTNET_AUTO_LOGS_NLOG_INSTRUMENTATION_ENABLED` to `false`.
78+
79+
Context injection is supported for `NLOG` in versions >= 5.0.0 && < 7.0.0
80+
81+
Following properties are set by default on the collection of logging event's properties:
82+
83+
- `trace_id`
84+
- `span_id`
85+
- `trace_flags`
86+
87+
This allows for trace context to be logged into currently configured log destination,
88+
e.g. a file. In order to use them, pattern needs to be updated.

docs/nlog-bridge.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# `NLog` [logs bridge](https://opentelemetry.io/docs/specs/otel/glossary/#log-appender--bridge)
2+
3+
> [!IMPORTANT]
4+
> NLog bridge is an experimental feature.
5+
6+
The `NLog` logs bridge is disabled by default. In order to enable it,
7+
set `OTEL_DOTNET_AUTO_LOGS_ENABLE_NLOG_BRIDGE` to `true`.
8+
9+
Bridge is supported for `NLOG` in versions >= 5.0.0 && < 7.0.0
10+
11+
If `NLOG` is used as a [logging provider](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging-providers),
12+
`NLOG` bridge should not be enabled, in order to reduce possibility of
13+
duplicated logs export.
14+
15+
## `NLog` logging events conversion
16+
17+
`NLog`'s `ILoggingEvent`s are converted to OpenTelemetry log records in
18+
a following way:
19+
20+
- `TimeStamp` is set as a `Timestamp`
21+
- `Level.Name` is set as a `SeverityText`
22+
- `FormattedMessage` is set as a `Body` if it is available
23+
- Otherwise, `Message` is set as a `Body`
24+
- `LoggerName` is set as an `InstrumentationScope.Name`
25+
- `GetProperties()`, apart from builtin properties prefixed with `nlog:`, `NLog.`,
26+
are added as attributes
27+
- `Exception` is used to populate the following properties: `exception.type`,
28+
`exception.message`, `exception.stacktrace`
29+
- `Level.Value` is mapped to `SeverityNumber` as outlined in the next section
30+
31+
### `NLog` level severity mapping
32+
33+
`NLog` levels are mapped to OpenTelemetry severity types according to
34+
following rules based on their numerical values.
35+
36+
Levels with numerical values of:
37+
38+
- Equal to `LogLevel.Fatal` is mapped to `LogRecordSeverity.Fatal`
39+
- Equal to `LogLevel.Error` is mapped to `LogRecordSeverity.Error`
40+
- Equal to `LogLevel.Warn` is mapped to `LogRecordSeverity.Warn`
41+
- Equal to `LogLevel.Info` is mapped to `LogRecordSeverity.Info`
42+
- Equal to `LogLevel.Debug` is mapped to `LogRecordSeverity.Debug`
43+
- Equal to `LogLevel.Trace` is mapped to `LogRecordSeverity.Trace`
44+
- Equal to `LogLevel.Off` is mapped to `LogRecordSeverity.Trace`
45+
- Any other is mapped to `LogRecordSeverity.Info`.
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
1+
OpenTelemetry.AutoInstrumentation.Instrumentations.NLog.TraceContextInjection.Integrations.NLogWriteToTargetsIntegration
2+
OpenTelemetry.AutoInstrumentation.Instrumentations.NLog.TraceContextInjection.Integrations.NLogWriteToTargetsWithWrapperTypeIntegration

0 commit comments

Comments
 (0)