Skip to content

Commit 947f03c

Browse files
authored
Merge pull request #55 from tbdrake/trace-and-span-ids
Add traceId and spanId properties in LogstashJsonFormatter output
2 parents 05dff48 + 653f3ca commit 947f03c

File tree

2 files changed

+103
-4
lines changed

2 files changed

+103
-4
lines changed

Serilog.Sinks.Network.Test/JsonFormatter.cs

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
using System;
2+
using System.Diagnostics;
23
using System.Net;
34
using System.Net.Sockets;
45
using System.Threading.Tasks;
56
using FluentAssertions;
7+
using Serilog.Core;
8+
using Serilog.Core.Enrichers;
9+
using Serilog.Events;
610
using Serilog.Formatting;
711
using Serilog.Sinks.Network.Formatters;
812
using Xunit;
@@ -11,15 +15,22 @@ namespace Serilog.Sinks.Network.Test
1115
{
1216
public class JsonFormatter
1317
{
14-
private static LoggerAndSocket ConfigureTestLogger(ITextFormatter? formatter = null)
18+
private static LoggerAndSocket ConfigureTestLogger(
19+
ITextFormatter? formatter = null,
20+
ILogEventEnricher[] enrichers = null
21+
)
1522
{
1623
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
1724
socket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
1825
socket.Listen();
1926

20-
var logger = new LoggerConfiguration()
21-
.WriteTo.TCPSink(IPAddress.Loopback, ((IPEndPoint)socket.LocalEndPoint!).Port, null, null, formatter)
22-
.CreateLogger();
27+
var loggerConfiguration = new LoggerConfiguration()
28+
.WriteTo.TCPSink(IPAddress.Loopback, ((IPEndPoint)socket.LocalEndPoint!).Port, null, null, formatter);
29+
if (enrichers != null)
30+
{
31+
loggerConfiguration.Enrich.With(enrichers);
32+
}
33+
var logger = loggerConfiguration.CreateLogger();
2334

2435
return new LoggerAndSocket { Logger = logger, Socket = socket };
2536
}
@@ -51,5 +62,81 @@ public async Task CanStillLogMessagesWithExceptions()
5162

5263
receivedData.Should().Contain("\"exception\":\"System.Exception: exploding\"}");
5364
}
65+
66+
[Fact]
67+
public async Task IncludesCurrentActivityTraceAndSpanIds()
68+
{
69+
// Create an ActivitySource, add a listener, and start an activity.
70+
// StartActivity() would return null if there were no listeners.
71+
using var activitySource = new ActivitySource("TestSource");
72+
using var activityListener = CreateAndAddActivityListener(activitySource.Name);
73+
using var activity = activitySource.StartActivity();
74+
Assert.NotNull(activity);
75+
76+
using var fixture = ConfigureTestLogger(new LogstashJsonFormatter());
77+
78+
fixture.Logger.Information("arbitraryMessage");
79+
80+
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
81+
82+
receivedData.Should().Contain($"\"traceId\":\"{activity.TraceId}\"");
83+
receivedData.Should().Contain($"\"spanId\":\"{activity.SpanId}\"");
84+
}
85+
86+
[Fact]
87+
public async Task OmitsTraceAndSpanIdsWhenThereIsNoActivity()
88+
{
89+
using var fixture = ConfigureTestLogger(new LogstashJsonFormatter());
90+
91+
fixture.Logger.Information("arbitraryMessage");
92+
93+
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
94+
95+
receivedData.Should().NotContain("\"traceId\"");
96+
receivedData.Should().NotContain("\"spanId\"");
97+
}
98+
99+
// The following test documents and validates the current behavior, but this could change
100+
// depending on how https://github.com/serilog-contrib/Serilog.Sinks.Network/issues/39 is
101+
// resolved.
102+
[Fact]
103+
public async Task WritesTraceAndSpanIdsBeforeDuplicatePropertiesFromEnrichers()
104+
{
105+
using var activitySource = new ActivitySource("TestSource");
106+
using var activityListener = CreateAndAddActivityListener(activitySource.Name);
107+
using var activity = activitySource.StartActivity();
108+
Assert.NotNull(activity);
109+
110+
using var fixture = ConfigureTestLogger(
111+
new LogstashJsonFormatter(),
112+
[
113+
new PropertyEnricher("traceId", "traceId-from-enricher"),
114+
new PropertyEnricher("spanId", "spanId-from-enricher")
115+
]
116+
);
117+
118+
fixture.Logger.Information("arbitraryMessage");
119+
120+
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
121+
122+
var indexOfTraceId = receivedData.IndexOf($"\"traceId\":\"{activity.TraceId}\"");
123+
var indexOfTraceIdFromEnricher = receivedData.IndexOf("\"traceId\":\"traceId-from-enricher\"");
124+
indexOfTraceId.Should().BePositive().And.BeLessThan(indexOfTraceIdFromEnricher);
125+
126+
var indexOfSpanId = receivedData.IndexOf($"\"spanId\":\"{activity.SpanId}\"");
127+
var indexOfSpanIdFromEnricher = receivedData.IndexOf("\"spanId\":\"spanId-from-enricher\"");
128+
indexOfSpanId.Should().BePositive().And.BeLessThan(indexOfSpanIdFromEnricher);
129+
}
130+
131+
private static ActivityListener CreateAndAddActivityListener(string sourceName)
132+
{
133+
var activityListener = new ActivityListener
134+
{
135+
ShouldListenTo = source => source.Name == sourceName,
136+
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData,
137+
};
138+
ActivitySource.AddActivityListener(activityListener);
139+
return activityListener;
140+
}
54141
}
55142
}

Serilog.Sinks.Network/Formatters/LogstashJsonFormatter.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ private static void FormatContent(LogEvent logEvent, TextWriter output)
4747

4848
WritePropertyAndValue(output, "message", logEvent.MessageTemplate.Render(logEvent.Properties));
4949

50+
if (logEvent.TraceId != null)
51+
{
52+
output.Write(",");
53+
WritePropertyAndValue(output, "traceId", logEvent.TraceId.Value.ToString());
54+
}
55+
56+
if (logEvent.SpanId != null)
57+
{
58+
output.Write(",");
59+
WritePropertyAndValue(output, "spanId", logEvent.SpanId.Value.ToString());
60+
}
61+
5062
if (logEvent.Exception != null)
5163
{
5264
output.Write(",");

0 commit comments

Comments
 (0)