11using System ;
22using System . Diagnostics ;
3+ using System . Linq ;
34using System . Net ;
45using System . Net . Sockets ;
56using System . Threading . Tasks ;
67using FluentAssertions ;
8+ using Serilog . Core ;
9+ using Serilog . Events ;
710using Serilog . Formatting ;
811using Serilog . Sinks . Network . Formatters ;
912using Xunit ;
@@ -12,15 +15,19 @@ namespace Serilog.Sinks.Network.Test
1215{
1316 public class JsonFormatter
1417 {
15- private static LoggerAndSocket ConfigureTestLogger ( ITextFormatter ? formatter = null )
18+ private static LoggerAndSocket ConfigureTestLogger ( ITextFormatter ? formatter = null , ILogEventEnricher [ ] ? enrichers = null )
1619 {
1720 var socket = new Socket ( AddressFamily . InterNetwork , SocketType . Stream , ProtocolType . Tcp ) ;
1821 socket . Bind ( new IPEndPoint ( IPAddress . Loopback , 0 ) ) ;
1922 socket . Listen ( ) ;
2023
21- var logger = new LoggerConfiguration ( )
22- . WriteTo . TCPSink ( IPAddress . Loopback , ( ( IPEndPoint ) socket . LocalEndPoint ! ) . Port , null , null , formatter )
23- . CreateLogger ( ) ;
24+ var loggerConfiguration = new LoggerConfiguration ( )
25+ . WriteTo . TCPSink ( IPAddress . Loopback , ( ( IPEndPoint ) socket . LocalEndPoint ! ) . Port , null , null , formatter ) ;
26+ if ( enrichers != null )
27+ {
28+ loggerConfiguration . Enrich . With ( enrichers ) ;
29+ }
30+ var logger = loggerConfiguration . CreateLogger ( ) ;
2431
2532 return new LoggerAndSocket { Logger = logger , Socket = socket } ;
2633 }
@@ -86,6 +93,44 @@ public async Task OmitsTraceAndSpanIdsWhenThereIsNoActivity()
8693 receivedData . Should ( ) . NotContain ( "\" spanId\" " ) ;
8794 }
8895
96+ [ Fact ]
97+ public async Task DoesNotAddDuplicateTraceAndSpanIds ( )
98+ {
99+ using var activitySource = new ActivitySource ( "TestSource" ) ;
100+ using var activityListener = CreateAndAddActivityListener ( activitySource . Name ) ;
101+ using Activity ? activity = activitySource . StartActivity ( ) ;
102+ Assert . NotNull ( activity ) ;
103+
104+ using var fixture = ConfigureTestLogger (
105+ new LogstashJsonFormatter ( ) ,
106+ // This enricher will add traceId and spanId properties to the log event:
107+ [ new TraceAndSpanEnricher ( ) ]
108+ ) ;
109+
110+ fixture . Logger . Information ( "arbitraryMessage" ) ;
111+
112+ var receivedData = await ServerPoller . PollForReceivedData ( fixture . Socket ) ;
113+
114+ // Count the occurrences of traceId and spanId in the received data:
115+ var traceIdCount = receivedData . Split ( "\" traceId\" " ) . Length - 1 ;
116+ traceIdCount . Should ( ) . Be ( 1 , "traceId should only appear once in the log message." ) ;
117+ var spanIdCount = receivedData . Split ( "\" spanId\" " ) . Length - 1 ;
118+ spanIdCount . Should ( ) . Be ( 1 , "spanId should only appear once in the log message." ) ;
119+ }
120+
121+ private class TraceAndSpanEnricher : ILogEventEnricher
122+ {
123+ public void Enrich ( LogEvent logEvent , ILogEventPropertyFactory propertyFactory )
124+ {
125+ var currentActivity = Activity . Current ;
126+ if ( currentActivity != null )
127+ {
128+ logEvent . AddOrUpdateProperty ( propertyFactory . CreateProperty ( "traceId" , currentActivity . TraceId . ToString ( ) ) ) ;
129+ logEvent . AddOrUpdateProperty ( propertyFactory . CreateProperty ( "spanId" , currentActivity . SpanId . ToString ( ) ) ) ;
130+ }
131+ }
132+ }
133+
89134 private static ActivityListener CreateAndAddActivityListener ( string sourceName )
90135 {
91136 var activityListener = new ActivityListener
0 commit comments