With .NET framework, we had this in the configuration, so you could trace the System.Net
and System.Net.Sockets
namespaces, but how can we do it in .NET Core?
I've tried without any luck.
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="System.Net" maxdatasize="1024">
<listeners>
<add name="TraceFile"/>
</listeners>
</source>
<source name="System.Net.Sockets" maxdatasize="1024">
<listeners>
<add name="TraceFile"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener"
initializeData="trace.log"/>
</sharedListeners>
<switches>
<add name="System.Net" value="Verbose" />
<add name="System.Net.Sockets" value="Verbose" />
</switches>
</system.diagnostics>
Low-level tracing of HTTP events isn't the best way to troubleshoot TLS issues because they're too low-level. I suspect the real problem is a mismatch in algorithms, otherwise the exception message would explain what's wrong, eg an untrusted or expired certificate.
An easier option is to use a debugging proxy like Fiddler which captures and translates the TLS handshake, showing what algorithms were requested.
As for tracing, it's evolved quite a bit since .NET Framework and since .NET 6 Tracing can publish to various targets, including OpenTelemetry systems as long as the correct sources are configured. As the linked article shows though, an easier but more expensive solution is to create an EventListener
in the code which writes directly to console or ILogger, or anything else we want.
The blog example listens to System.Net.Http
events. That's not enough to capture TLS algorithm negotiation though. This Github Issue shows how to listen to the internal Private.InternalDiagnostics.System.Net.Http
source. This results in a lot of very detailed messages, but they do include algorithms, thumbnails etc:
using System.Diagnostics;
using System.Diagnostics.Tracing;
internal sealed class HttpEventListener : EventListener
{
protected override void OnEventSourceCreated(EventSource eventSource)
{
// Allow internal HTTP logging
if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http")
{
EnableEvents(eventSource, EventLevel.LogAlways);
}
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
// Log whatever other properties you want, this is just an example
var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff}[{eventData.EventName}] ");
for (int i = 0; i < eventData.Payload?.Count; i++)
{
if (i > 0)
sb.Append(", ");
sb.Append(eventData.PayloadNames?[i]).Append(": ").Append(eventData.Payload[i]);
}
try {
Console.WriteLine(sb.ToString());
} catch { }
}
}
With that class you can start listening to low-level events :
var listener=new HttpEventListener();
var hc=new HttpClient();
await hc.GetStringAsync("https://dotnet.microsoft.com/");
This generates a lot of events, but one of them is this
08:34:11.4330369[HandlerMessage] poolId: 27011992, workerId: 6576978, requestId: 0, memberName: TraceConnection,
message: HttpConnection(HttpConnectionPool https://dotnet.microsoft.com:443). SslProtocol:Tls12,
NegotiatedApplicationProtocol:, NegotiatedCipherSuite:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherAlgorithm:Aes256,
CipherStrength:256, HashAlgorithm:Sha384, HashStrength:0, KeyExchangeAlgorithm:44550, KeyExchangeStrength:256,
LocalCertificate:, RemoteCertificate:[Subject] CN=dotnet.microsoft.com, O=Microsoft Corporation, L=Redmond, S=WA, C=US ...
To listen to events from other libraries, modify the check in OnEventSourceCreated
. You can find even the private event sources by checking the .NET repo directly. The event source for System.Net.Sockets is defined in NetEventSource.Sockets.cs and its name is Private.InternalDiagnostics.System.Net.Sockets
. That means OnEventSourceCreated
can be changed to :
var monitoredSources=new[]{
"Private.InternalDiagnostics.System.Net.Http",
"Private.InternalDiagnostics.System.Net.Sockets"
};
if (monitoredSources.Contains(eventSource.Name))
{
EnableEvents(eventSource, EventLevel.LogAlways);
}
Or even this, to record all System.Net diagnostic events
if (eventSource.Name.StartsWith("Private.InternalDiagnostics.System.Net"))
{
EnableEvents(eventSource, EventLevel.LogAlways);
}