Skip to content

Fix infinite hang #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PingPonger/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static int Main(string[] args)
}
if (options.TCP)
{
logConfig.WriteTo.TCPSink(options.Url, options.Port, new LogstashJsonFormatter());
logConfig.WriteTo.TCPSink(options.Url, options.Port, null, null, new LogstashJsonFormatter());
}
}
else if (options.IP.Length > 0)
Expand All @@ -86,7 +86,7 @@ public static int Main(string[] args)
}
if (options.TCP)
{
logConfig.WriteTo.TCPSink(ipAddress, options.Port, new LogstashJsonFormatter());
logConfig.WriteTo.TCPSink(ipAddress, options.Port,null,null, new LogstashJsonFormatter());
}
}
else
Expand Down
37 changes: 18 additions & 19 deletions Serilog.Sinks.Network.Test/JsonFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using FluentAssertions;
using Serilog.Core;
using Serilog.Formatting;
using Serilog.Sinks.Network.Formatters;
using Xunit;
Expand All @@ -10,45 +11,43 @@ namespace Serilog.Sinks.Network.Test
{
public class JsonFormatter
{
private TCPServer _server;
private Logger _logger;


private void ConfigureTestLogger(ITextFormatter formatter = null)
private static LoggerAndSocket ConfigureTestLogger(ITextFormatter formatter = null)
{
var port = new Random().Next(50003) + 10000;
_server = new TCPServer(IPAddress.Loopback, port);
_server.Start();
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
socket.Listen();

_logger = new LoggerConfiguration()
.WriteTo.TCPSink(IPAddress.Loopback, port, formatter)
var logger = new LoggerConfiguration()
.WriteTo.TCPSink(IPAddress.Loopback, ((IPEndPoint)socket.LocalEndPoint!).Port, null, null, formatter)
.CreateLogger();

return new LoggerAndSocket { Logger = logger, Socket = socket };
}

[Fact]
public void MustNotLogATrailingCommaWhenThereAreNoProperties()
public async Task MustNotLogATrailingCommaWhenThereAreNoProperties()
{
ConfigureTestLogger(new LogstashJsonFormatter());
using var fixture = ConfigureTestLogger(new LogstashJsonFormatter());
var arbitraryMessage = nameof(JsonFormatter) + "MustNotLogATrailingCommaWhenThereAreNoProperties" + Guid.NewGuid();

_logger.Information(arbitraryMessage);
fixture.Logger.Information(arbitraryMessage);

var receivedData = ServerPoller.PollForReceivedData(_server);
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
var loggedData = receivedData?.TrimEnd('\n');

var logMessageWithTrailingComma = $"\"message\":\"{arbitraryMessage}\",}}";
loggedData.Should().NotEndWith(logMessageWithTrailingComma);
}

[Fact]
public void CanStillLogMessagesWithExceptions()
public async Task CanStillLogMessagesWithExceptions()
{
ConfigureTestLogger(new LogstashJsonFormatter());
using var fixture = ConfigureTestLogger(new LogstashJsonFormatter());
var arbitraryMessage = nameof(JsonFormatter) + "CanStillLogMessagesWithExceptions" + Guid.NewGuid();

_logger.Information(new Exception("exploding"), arbitraryMessage);
fixture.Logger.Information(new Exception("exploding"), arbitraryMessage);

var receivedData = ServerPoller.PollForReceivedData(_server);
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);

receivedData.Should().Contain("\"exception\":\"System.Exception: exploding\"}");
}
Expand Down
15 changes: 15 additions & 0 deletions Serilog.Sinks.Network.Test/LoggerAndSocket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Net.Sockets;

namespace Serilog.Sinks.Network.Test
{
public record LoggerAndSocket : System.IDisposable
{
public required ILogger Logger;
public required Socket Socket;
public void Dispose()
{
Socket.Dispose();
}
}

}
47 changes: 32 additions & 15 deletions Serilog.Sinks.Network.Test/ServerPoller.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Serilog.Sinks.Network.Test
{
internal static class ServerPoller
{
public static string PollForReceivedData(DataReceiver dataReceiver)
public static async Task<string> PollForReceivedData(Socket socket, bool udp = false)
{
var stopwatch = Stopwatch.StartNew();
string receivedData = null;
while (string.IsNullOrEmpty(receivedData))
var buffer = new byte[1000];
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30.0));
var result = new List<byte>();

Socket clientSocket;
if (udp)
{
clientSocket = socket;
}
else
{
receivedData = dataReceiver.ReceivedData.SingleOrDefault();
if (stopwatch.Elapsed > TimeSpan.FromSeconds(5))
clientSocket = await socket.AcceptAsync(cts.Token);
}
var isDone = false;
while (!isDone)
{
int readResult = await clientSocket.ReceiveAsync(buffer, SocketFlags.None, cts.Token);
for (var i = 0; i < readResult; i++)
{
throw new NoDataReceivedWithinFiveSeconds();
result.Add(buffer[i]);
}
}

return receivedData;
if (readResult < buffer.Length)
{
isDone = true;
}
}

return Encoding.ASCII.GetString(result.ToArray());
}
}

internal class NoDataReceivedWithinFiveSeconds : Exception
{
}
}
78 changes: 0 additions & 78 deletions Serilog.Sinks.Network.Test/WhenLoggingViaTCP.cs

This file was deleted.

79 changes: 79 additions & 0 deletions Serilog.Sinks.Network.Test/WhenLoggingViaTcp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Dynamic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Execution;
using Newtonsoft.Json;
using Serilog.Formatting;
using Serilog.Formatting.Compact;
using Serilog.Sinks.Network.Formatters;
using Xunit;

namespace Serilog.Sinks.Network.Test
{
public class WhenLoggingViaTcp
{
private static LoggerAndSocket ConfigureTestLogger(ITextFormatter formatter = null)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
socket.Listen();

var logger = new LoggerConfiguration()
.WriteTo.TCPSink(IPAddress.Loopback, ((IPEndPoint)socket.LocalEndPoint!).Port, null, null, formatter)
.CreateLogger();

return new LoggerAndSocket { Logger = logger, Socket = socket };
}

[Fact]
public async Task CanLogHelloWorld_WithLogstashJsonFormatter()
{
using var fixture = ConfigureTestLogger(new LogstashJsonFormatter());
var arbitraryMessage = nameof(WhenLoggingViaTcp) + "CanLogHelloWorld_WithLogstashJsonFormatter" + Guid.NewGuid();
fixture.Logger.Information(arbitraryMessage);
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
receivedData.Should().Contain($"\"message\":\"{arbitraryMessage}\"");
}

[Fact]
public async Task CanLogHelloWorld_WithDefaultFormatter()
{
using var fixture = ConfigureTestLogger();
var arbitraryMessage = nameof(WhenLoggingViaTcp) + "CanLogHelloWorld_WithDefaultFormatter" + Guid.NewGuid();
fixture.Logger.Information(arbitraryMessage);

var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);

receivedData.Should().Contain($"\"message\":\"{arbitraryMessage}\"");
}

[Fact]
public async Task CanLogHelloWorld_WithRawFormatter()
{
using var fixture =ConfigureTestLogger(new CompactJsonFormatter());
var arbitraryMessage = nameof(WhenLoggingViaTcp) + "CanLogHelloWorld_WithCompactJsonFormatter" + Guid.NewGuid();
fixture.Logger.Information(arbitraryMessage);
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
receivedData.Should().Contain($"\"{arbitraryMessage}\"");
}

[Fact]
public async Task CanLogWithProperties()
{
using var fixture = ConfigureTestLogger();
fixture.Logger.Information("TCP Hello {location}", "world");
var receivedData = await ServerPoller.PollForReceivedData(fixture.Socket);
dynamic payload = JsonConvert.DeserializeObject<ExpandoObject>(receivedData);
if (payload == null)
{
throw new AssertionFailedException("expected payload not null");
}
Assert.Equal("Information", payload.level);
Assert.Equal("TCP Hello \"world\"", payload.message);
Assert.Equal("world", payload.location);
}
}
}
Loading