Skip to content

Commit e4f9cb8

Browse files
committed
feat(server): add UseConnectionLogging() and wire through to ConnectionActor
1 parent 5f4f450 commit e4f9cb8

7 files changed

Lines changed: 88 additions & 10 deletions

File tree

src/TurboHTTP.Tests/Server/TurboListenOptionsSpec.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,29 @@ public void UseHttps_called_twice_should_use_last_configuration()
114114
Assert.Null(options.HttpsOptions.CertificatePath);
115115
}
116116

117+
[Fact(Timeout = 5000)]
118+
public void ConnectionLoggingCategory_should_be_null_by_default()
119+
{
120+
var options = new TurboListenOptions(IPAddress.Loopback, 80);
121+
Assert.Null(options.ConnectionLoggingCategory);
122+
}
123+
124+
[Fact(Timeout = 5000)]
125+
public void UseConnectionLogging_should_set_default_category()
126+
{
127+
var options = new TurboListenOptions(IPAddress.Loopback, 80);
128+
options.UseConnectionLogging();
129+
Assert.Equal("TurboHTTP.Server.ConnectionLogging", options.ConnectionLoggingCategory);
130+
}
131+
132+
[Fact(Timeout = 5000)]
133+
public void UseConnectionLogging_with_name_should_set_custom_category()
134+
{
135+
var options = new TurboListenOptions(IPAddress.Loopback, 80);
136+
options.UseConnectionLogging("MyApp.WireLog");
137+
Assert.Equal("MyApp.WireLog", options.ConnectionLoggingCategory);
138+
}
139+
117140
private static X509Certificate2 CreateSelfSignedCert()
118141
{
119142
using var rsa = System.Security.Cryptography.RSA.Create(2048);

src/TurboHTTP/Server/Hosting/TurboServerHostedService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public async Task StartAsync(CancellationToken cancellationToken)
7171
pipeline,
7272
routeTable,
7373
_services,
74-
materializer));
74+
materializer,
75+
endpoint.ConnectionLoggingCategory));
7576
}
7677

7778
_supervisor = _system.ActorOf(

src/TurboHTTP/Server/Internal/EndpointResolver.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ private static ListenerBinding CreateTcpBinding(TurboListenOptions listen, X509C
195195
return new ListenerBinding
196196
{
197197
Options = tcpOptions,
198-
Factory = new TcpListenerFactory()
198+
Factory = new TcpListenerFactory(),
199+
ConnectionLoggingCategory = listen.ConnectionLoggingCategory
199200
};
200201
}
201202

@@ -215,7 +216,8 @@ private static ListenerBinding CreateQuicBinding(TurboListenOptions listen, X509
215216
return new ListenerBinding
216217
{
217218
Options = quicOptions,
218-
Factory = new QuicListenerFactory()
219+
Factory = new QuicListenerFactory(),
220+
ConnectionLoggingCategory = listen.ConnectionLoggingCategory
219221
};
220222
}
221223
}

src/TurboHTTP/Server/ListenerBinding.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ public sealed class ListenerBinding
66
{
77
public required ListenerOptions Options { get; init; }
88
public required IListenerFactory Factory { get; init; }
9+
public string? ConnectionLoggingCategory { get; init; }
910
}

src/TurboHTTP/Server/TurboListenOptions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,16 @@ public void UseHttps(string path, string? password, Action<TurboHttpsOptions> co
5252
};
5353
configure(HttpsOptions);
5454
}
55+
56+
internal string? ConnectionLoggingCategory { get; private set; }
57+
58+
public void UseConnectionLogging()
59+
{
60+
ConnectionLoggingCategory = "TurboHTTP.Server.ConnectionLogging";
61+
}
62+
63+
public void UseConnectionLogging(string loggerName)
64+
{
65+
ConnectionLoggingCategory = loggerName;
66+
}
5567
}

src/TurboHTTP/Streams/Lifecycle/ConnectionActor.cs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
using Akka.Event;
44
using Akka.Streams;
55
using Akka.Streams.Dsl;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Microsoft.Extensions.Logging;
68
using Servus.Akka.Transport;
9+
using TurboHTTP.Diagnostics;
710
using TurboHTTP.Routing;
811
using TurboHTTP.Server;
912
using TurboHTTP.Server.Middleware;
@@ -34,7 +37,8 @@ public sealed record Materialize(
3437
RouteTable RouteTable,
3538
TurboConnectionInfo ConnectionInfo,
3639
IServiceProvider Services,
37-
IMaterializer Materializer);
40+
IMaterializer Materializer,
41+
string? ConnectionLoggingCategory = null);
3842

3943
public sealed record GracefulStop(TimeSpan Timeout);
4044

@@ -82,9 +86,38 @@ private void OnMaterialize(Materialize msg)
8286
return item;
8387
});
8488

85-
var completionTask = msg.ConnectionFlow
89+
Flow<ITransportInbound, ITransportInbound, NotUsed>? loggingFlow = null;
90+
if (msg.ConnectionLoggingCategory is { } loggingCategory)
91+
{
92+
var loggerFactory = msg.Services.GetRequiredService<ILoggerFactory>();
93+
var logger = loggerFactory.CreateLogger(loggingCategory);
94+
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
95+
{
96+
loggingFlow = Flow.Create<ITransportInbound>()
97+
.Select(item =>
98+
{
99+
if (item is TransportData { Buffer: var buffer })
100+
{
101+
var dump = HexDumpFormatter.Format(buffer.Span);
102+
logger.LogDebug("ReadAsync[{Length}]{NewLine}{Dump}",
103+
buffer.Length, Environment.NewLine, dump);
104+
}
105+
106+
return item;
107+
});
108+
}
109+
}
110+
111+
var pipeline = msg.ConnectionFlow
86112
.Via(_killSwitch.Flow<ITransportInbound>())
87-
.Via(inboundTap)
113+
.Via(inboundTap);
114+
115+
if (loggingFlow is not null)
116+
{
117+
pipeline = pipeline.Via(loggingFlow);
118+
}
119+
120+
var completionTask = pipeline
88121
.ViaMaterialized(
89122
Flow.Create<ITransportInbound>().WatchTermination(Keep.Right),
90123
Keep.Right)

src/TurboHTTP/Streams/Lifecycle/ListenerActor.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal sealed class ListenerActor : ReceiveActor
2020
private readonly RouteTable _routeTable;
2121
private readonly IServiceProvider _services;
2222
private readonly IMaterializer _materializer;
23+
private readonly string? _connectionLoggingCategory;
2324

2425
private UniqueKillSwitch? _listenerKillSwitch;
2526
private int _connectionCounter;
@@ -48,7 +49,8 @@ public ListenerActor(
4849
TurboRequestDelegate pipeline,
4950
RouteTable routeTable,
5051
IServiceProvider services,
51-
IMaterializer materializer)
52+
IMaterializer materializer,
53+
string? connectionLoggingCategory = null)
5254
{
5355
_factory = factory;
5456
_listenerOptions = listenerOptions;
@@ -57,6 +59,7 @@ public ListenerActor(
5759
_routeTable = routeTable;
5860
_services = services;
5961
_materializer = materializer;
62+
_connectionLoggingCategory = connectionLoggingCategory;
6063

6164
Receive<StartListening>(_ => OnStartListening());
6265
Receive<IncomingConnection>(OnIncomingConnection);
@@ -120,7 +123,8 @@ private void OnIncomingConnection(IncomingConnection msg)
120123
_routeTable,
121124
connectionInfo,
122125
_services,
123-
_materializer));
126+
_materializer,
127+
_connectionLoggingCategory));
124128

125129
Context.Parent.Tell(new ConnectionStarted(connectionId, child));
126130
}
@@ -193,8 +197,10 @@ public static Props Create(
193197
TurboRequestDelegate pipeline,
194198
RouteTable routeTable,
195199
IServiceProvider services,
196-
IMaterializer materializer)
200+
IMaterializer materializer,
201+
string? connectionLoggingCategory = null)
197202
=> Props.Create(() => new ListenerActor(
198203
factory, listenerOptions, serverOptions,
199-
pipeline, routeTable, services, materializer));
204+
pipeline, routeTable, services, materializer,
205+
connectionLoggingCategory));
200206
}

0 commit comments

Comments
 (0)