diff --git a/NLog.Targets.OpenTelemetryProtocol.Test/Program.cs b/NLog.Targets.OpenTelemetryProtocol.Test/Program.cs
index 64b72af..653ab6b 100644
--- a/NLog.Targets.OpenTelemetryProtocol.Test/Program.cs
+++ b/NLog.Targets.OpenTelemetryProtocol.Test/Program.cs
@@ -1,4 +1,7 @@
-using System;
+using NLog.Targets.Wrappers;
+using System;
+using System.Diagnostics;
+using System.Linq;
using System.Threading;
namespace NLog.Targets.OpenTelemetryProtocol.Test
@@ -13,6 +16,7 @@ public static void Main()
using var currentActivity = new System.Diagnostics.Activity("Hello World").Start();
+ logger.Fatal("message: {messageField}", message);
logger.Fatal("message: {messageField}", message);
Thread.Sleep(10000);
diff --git a/NLog.Targets.OpenTelemetryProtocol.Test/nlog.config b/NLog.Targets.OpenTelemetryProtocol.Test/nlog.config
index b78e62d..c26d395 100644
--- a/NLog.Targets.OpenTelemetryProtocol.Test/nlog.config
+++ b/NLog.Targets.OpenTelemetryProtocol.Test/nlog.config
@@ -29,7 +29,6 @@
-
\ No newline at end of file
diff --git a/NLog.Targets.OpenTelemetryProtocol/ActivityExtensions.cs b/NLog.Targets.OpenTelemetryProtocol/ActivityExtensions.cs
new file mode 100644
index 0000000..f26e6d1
--- /dev/null
+++ b/NLog.Targets.OpenTelemetryProtocol/ActivityExtensions.cs
@@ -0,0 +1,52 @@
+using System.Diagnostics;
+
+namespace NLog.Targets.OpenTelemetryProtocol
+{
+ ///
+ /// Formats elements of for inclusion in log events. Non-W3C-format activities are
+ /// ignored (Seq does not support the older Microsoft-proprietary hierarchical activity id format).
+ ///
+ internal static class ActivityExtensions
+ {
+ private static readonly System.Diagnostics.ActivitySpanId EmptySpanId = default(System.Diagnostics.ActivitySpanId);
+ private static readonly System.Diagnostics.ActivityTraceId EmptyTraceId = default(System.Diagnostics.ActivityTraceId);
+
+ public static string GetSpanId(this Activity activity)
+ {
+ return activity.IdFormat == ActivityIdFormat.W3C ?
+ SpanIdToHexString(activity.SpanId) :
+ string.Empty;
+ }
+
+ public static string GetTraceId(this Activity activity)
+ {
+ return activity.IdFormat == ActivityIdFormat.W3C ?
+ TraceIdToHexString(activity.TraceId) :
+ string.Empty;
+ }
+
+ private static string SpanIdToHexString(ActivitySpanId spanId)
+ {
+ if (EmptySpanId.Equals(spanId))
+ return string.Empty;
+
+ var spanHexString = spanId.ToHexString();
+ if (ReferenceEquals(spanHexString, EmptySpanId.ToHexString()))
+ return string.Empty;
+
+ return spanHexString;
+ }
+
+ private static string TraceIdToHexString(ActivityTraceId traceId)
+ {
+ if (EmptyTraceId.Equals(traceId))
+ return string.Empty;
+
+ var traceHexString = traceId.ToHexString();
+ if (ReferenceEquals(traceHexString, EmptyTraceId.ToHexString()))
+ return string.Empty;
+
+ return traceHexString;
+ }
+ }
+}
\ No newline at end of file
diff --git a/NLog.Targets.OpenTelemetryProtocol/OtlpTarget.cs b/NLog.Targets.OpenTelemetryProtocol/OtlpTarget.cs
index b20282f..4b4e345 100644
--- a/NLog.Targets.OpenTelemetryProtocol/OtlpTarget.cs
+++ b/NLog.Targets.OpenTelemetryProtocol/OtlpTarget.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Globalization;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -11,6 +12,7 @@
using NLog.Layouts;
using NLog.Targets.OpenTelemetryProtocol;
using NLog.Targets.OpenTelemetryProtocol.Exceptions;
+using NLog.Targets.Wrappers;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
@@ -29,6 +31,7 @@ public class OtlpTarget : TargetWithContext
private readonly ConcurrentDictionary _loggers = new(StringComparer.Ordinal);
private readonly object _sync = new object();
+ private readonly object _sync2 = new object();
private const string OriginalFormatName = "{OriginalFormat}";
@@ -67,6 +70,30 @@ public class OtlpTarget : TargetWithContext
public bool DisableEventListener { get; set; }
+ public Layout TraceId { get; set; } = Layout.FromMethod(evt => System.Diagnostics.Activity.Current?.GetTraceId());
+
+ public Layout SpanId { get; set; } = Layout.FromMethod(evt => System.Diagnostics.Activity.Current?.GetSpanId());
+
+ private bool _checked = false;
+ private bool _isWrapped;
+
+ public bool IsWrapped
+ {
+ get
+ {
+ lock (_sync2)
+ {
+ if (!_checked)
+ {
+ _checked = true;
+ _isWrapped = LogManager.Configuration.AllTargets.Where(t => t is AsyncTargetWrapper).ToArray().Cast().Any(x => x.WrappedTarget == this);
+ }
+
+ return _isWrapped;
+ }
+ }
+ }
+
public OtlpTarget()
{
Layout = "${message}";
@@ -246,6 +273,16 @@ protected override void Write(LogEventInfo logEvent)
Timestamp = logEvent.TimeStamp,
};
+ if (IsWrapped)
+ {
+ var spanId = RenderLogEvent(SpanId, logEvent);
+ if (!string.IsNullOrEmpty(spanId))
+ data.SpanId = System.Diagnostics.ActivitySpanId.CreateFromString(spanId.AsSpan());
+ var traceId = RenderLogEvent(TraceId, logEvent);
+ if (!string.IsNullOrEmpty(traceId))
+ data.TraceId = System.Diagnostics.ActivityTraceId.CreateFromString(traceId.AsSpan());
+ }
+
if (IncludeFormattedMessage && (logEvent.Parameters?.Length > 0 || logEvent.HasProperties))
{
var formattedMessage = RenderLogEvent(Layout, logEvent);
diff --git a/UnitTests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj b/UnitTests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj
index e1ecbfa..ab99f54 100644
--- a/UnitTests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj
+++ b/UnitTests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj
@@ -60,6 +60,9 @@
+
+ Always
+
Always
diff --git a/UnitTests/TargetTests.cs b/UnitTests/TargetTests.cs
index 18af744..06d0475 100644
--- a/UnitTests/TargetTests.cs
+++ b/UnitTests/TargetTests.cs
@@ -503,6 +503,7 @@ public void ActivityContextIsPopulated()
using var currentActivity = new System.Diagnostics.Activity("Hello World").Start();
logger.Info(message);
+ Assert.False(target.IsWrapped);
OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), target.LogRecords[0]);
@@ -510,6 +511,31 @@ public void ActivityContextIsPopulated()
Assert.Equal(currentActivity.SpanId.ToString(), ByteStringToHexString(otlpLogRecord.SpanId));
}
+ [Fact]
+ public void ActivityContextIsPopulatedIfAsync()
+ {
+ LogManager.LoadConfiguration("nlog2.config");
+ var logger = LogManager.GetCurrentClassLogger();
+
+ OtlpTarget target = (OtlpTarget)LogManager.Configuration.AllTargets.First(x => x is OtlpTarget);
+ LogManager.ReconfigExistingLoggers();
+
+ var message = "message";
+
+ using var currentActivity = new System.Diagnostics.Activity("Hello World").Start();
+
+ logger.Info(message);
+
+ Thread.Sleep(10000);
+
+ Assert.True(target.IsWrapped);
+ Assert.Single(target.LogRecords);
+ OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), target.LogRecords[0]);
+
+ Assert.Equal(currentActivity.TraceId.ToString(), ByteStringToHexString(otlpLogRecord.TraceId));
+ Assert.Equal(currentActivity.SpanId.ToString(), ByteStringToHexString(otlpLogRecord.SpanId));
+ }
+
private string ByteStringToHexString(Google.Protobuf.ByteString str)
{
return BitConverter.ToString(str.ToByteArray()).Replace("-", "").ToLower();
diff --git a/UnitTests/nlog2.config b/UnitTests/nlog2.config
new file mode 100644
index 0000000..b20d968
--- /dev/null
+++ b/UnitTests/nlog2.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file