diff --git a/src/Elastic.CommonSchema.NLog/ActivityExtensions.cs b/src/Elastic.CommonSchema.NLog/ActivityExtensions.cs new file mode 100644 index 00000000..f2805975 --- /dev/null +++ b/src/Elastic.CommonSchema.NLog/ActivityExtensions.cs @@ -0,0 +1,51 @@ +using System.Diagnostics; + +namespace Elastic.CommonSchema.NLog +{ + /// + /// Helpers for getting the right values from Activity no matter the format (w3c or hierarchical) + /// + internal static class ActivityExtensions + { + private static readonly ActivitySpanId EmptySpanId = default; + private static readonly ActivityTraceId EmptyTraceId = default; + + public static string GetSpanId(this Activity activity) => + activity.IdFormat == ActivityIdFormat.W3C ? + SpanIdToHexString(activity.SpanId) : + activity.Id; + + public static string GetTraceId(this Activity activity) => + activity.IdFormat == ActivityIdFormat.W3C ? + TraceIdToHexString(activity.TraceId) : + activity.RootId; + + public static string GetParentId(this Activity activity) => + activity.IdFormat == ActivityIdFormat.W3C ? + SpanIdToHexString(activity.ParentSpanId) : + activity.ParentId; + + 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(); + return ReferenceEquals(traceHexString, EmptyTraceId.ToHexString()) + ? string.Empty + : traceHexString; + } + } +} diff --git a/src/Elastic.CommonSchema.NLog/EcsLayout.cs b/src/Elastic.CommonSchema.NLog/EcsLayout.cs index d824040c..70e76be0 100644 --- a/src/Elastic.CommonSchema.NLog/EcsLayout.cs +++ b/src/Elastic.CommonSchema.NLog/EcsLayout.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.Serialization; using System.Text; using System.Text.Json.Serialization; @@ -21,6 +22,7 @@ internal class NlogEcsDocumentCreationOptions : IEcsDocumentCreationOptions public bool IncludeHost { get; set; } = false; public bool IncludeProcess { get; set; } = false; public bool IncludeUser { get; set; } = false; + public bool IncludeTraceId { get; set; } = false; } /// An NLOG layout implementation that renders logs as ECS json @@ -146,11 +148,11 @@ private static bool NLogWeb4Registered() => // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global /// - public Layout ApmTraceId { get; set; } + public Layout ApmTraceId { get; set; } = FromMethod(_ => ResolveTraceId()); /// public Layout ApmTransactionId { get; set; } /// - public Layout ApmSpanId { get; set; } + public Layout ApmSpanId { get; set; } = FromMethod(_ => ResolveSpanId()); /// public Layout ApmServiceName { get; set; } @@ -775,6 +777,10 @@ private static void Populate(IDictionary propertyBag, string key propertyBag.Add(usedKey, value); } + private static string ResolveTraceId() => Activity.Current?.GetTraceId(); + + private static string ResolveSpanId() => Activity.Current?.GetSpanId(); + /// /// A subclass of that adds additional properties related to Extensions logging. /// For instance it adds scope information to each logged event diff --git a/src/Elastic.CommonSchema.Serilog/EcsTextFormatterConfiguration.cs b/src/Elastic.CommonSchema.Serilog/EcsTextFormatterConfiguration.cs index b67bccf7..4416119e 100644 --- a/src/Elastic.CommonSchema.Serilog/EcsTextFormatterConfiguration.cs +++ b/src/Elastic.CommonSchema.Serilog/EcsTextFormatterConfiguration.cs @@ -57,6 +57,9 @@ public class EcsTextFormatterConfiguration : IEcsTextFormatterConf /// public bool IncludeUser { get; set; } = true; + /// + public bool IncludeTraceId { get; set; } = true; + /// public IHttpAdapter? MapHttpAdapter { get; set; } diff --git a/src/Elastic.CommonSchema/EcsDocument.cs b/src/Elastic.CommonSchema/EcsDocument.cs index 23fbb888..2fdb9ad6 100644 --- a/src/Elastic.CommonSchema/EcsDocument.cs +++ b/src/Elastic.CommonSchema/EcsDocument.cs @@ -34,6 +34,11 @@ public interface IEcsDocumentCreationOptions /// Gets or sets a flag indicating whether user details should be included in the message. Defaults to true. /// bool IncludeUser { get; set; } + + /// + /// Gets or sets a flag indicating whether TraceId/SpanId should be included in the message. Defaults to true. + /// + bool IncludeTraceId { get; set; } } /// @@ -85,11 +90,12 @@ public static TEcsDocument CreateNewWithDefaults( Error = GetError(exception), Service = GetService(initialCache) }; - SetActivityData(doc); if (options?.IncludeHost is null or true) doc.Host = GetHost(initialCache); if (options?.IncludeProcess is null or true) doc.Process = GetProcess(initialCache); if (options?.IncludeUser is null or true) doc.User = GetUser(); + if (options?.IncludeTraceId is null or true) + SetActivityData(doc); return doc; } diff --git a/src/Elastic.Extensions.Logging/Options/ElasticsearchLoggerOptions.cs b/src/Elastic.Extensions.Logging/Options/ElasticsearchLoggerOptions.cs index 35793e6b..40c547c5 100644 --- a/src/Elastic.Extensions.Logging/Options/ElasticsearchLoggerOptions.cs +++ b/src/Elastic.Extensions.Logging/Options/ElasticsearchLoggerOptions.cs @@ -29,6 +29,11 @@ public class ElasticsearchLoggerOptions : IEcsDocumentCreationOptions /// public bool IncludeUser { get; set; } = true; + /// + /// Gets or sets a flag indicating whether TraceId/SpanId should be included in the message. Defaults to true. + /// + public bool IncludeTraceId { get; set; } = true; + /// /// The data stream to log into, defaults to logs-generic-default if neither or is set. ///