diff --git a/src/libraries/Common/tests/TestUtilities/TestEventListener.cs b/src/libraries/Common/tests/TestUtilities/TestEventListener.cs
new file mode 100644
index 00000000000000..e41987992c80b6
--- /dev/null
+++ b/src/libraries/Common/tests/TestUtilities/TestEventListener.cs
@@ -0,0 +1,109 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using System.IO;
+using System.Text;
+using Xunit.Abstractions;
+
+namespace TestUtilities;
+
+///
+/// Logging helper for tests.
+/// Logs event source events into test output.
+/// Example usage:
+/// // Put the following line into your test method:
+/// using var listener = new TestEventListener(_output, TestEventListener.NetworkingEvents);
+///
+public sealed class TestEventListener : EventListener
+{
+ public static string[] NetworkingEvents => new[]
+ {
+ "System.Net.Http",
+ "System.Net.NameResolution",
+ "System.Net.Sockets",
+ "System.Net.Security",
+ "System.Net.TestLogging",
+ "Private.InternalDiagnostics.System.Net.Http",
+ "Private.InternalDiagnostics.System.Net.NameResolution",
+ "Private.InternalDiagnostics.System.Net.Sockets",
+ "Private.InternalDiagnostics.System.Net.Security",
+ "Private.InternalDiagnostics.System.Net.Quic",
+ "Private.InternalDiagnostics.System.Net.Http.WinHttpHandler",
+ "Private.InternalDiagnostics.System.Net.HttpListener",
+ "Private.InternalDiagnostics.System.Net.Mail",
+ "Private.InternalDiagnostics.System.Net.NetworkInformation",
+ "Private.InternalDiagnostics.System.Net.Ping",
+ "Private.InternalDiagnostics.System.Net.Primitives",
+ "Private.InternalDiagnostics.System.Net.Requests",
+ };
+
+ private readonly Action _writeFunc;
+ private readonly HashSet _sourceNames;
+
+ // Until https://github.com/dotnet/runtime/issues/63979 is solved.
+ private List _eventSources = new List();
+
+ public TestEventListener(TextWriter output, params string[] sourceNames)
+ : this(str => output.WriteLine(str), sourceNames)
+ { }
+
+ public TestEventListener(ITestOutputHelper output, params string[] sourceNames)
+ : this(str => output.WriteLine(str), sourceNames)
+ { }
+
+ public TestEventListener(Action writeFunc, params string[] sourceNames)
+ {
+ lock (this)
+ {
+ _writeFunc = writeFunc;
+ _sourceNames = new HashSet(sourceNames);
+ foreach (var eventSource in _eventSources)
+ {
+ OnEventSourceCreated(eventSource);
+ }
+ _eventSources = null;
+ }
+ }
+
+ protected override void OnEventSourceCreated(EventSource eventSource)
+ {
+ lock (this)
+ {
+ // We're called from base ctor, just save the event source for later initialization.
+ if (_sourceNames is null)
+ {
+ _eventSources.Add(eventSource);
+ return;
+ }
+
+ // Second pass called from our ctor, allow logging for specified source names.
+ if (_sourceNames.Contains(eventSource.Name))
+ {
+ EnableEvents(eventSource, EventLevel.LogAlways);
+ }
+ }
+ }
+
+ protected override void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+#if NETCOREAPP2_2_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff}[{eventData.EventName}] ");
+#else
+ var sb = new StringBuilder().Append($"[{eventData.EventName}] ");
+#endif
+ 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
+ {
+ _writeFunc(sb.ToString());
+ }
+ catch { }
+ }
+}
diff --git a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
index 657ccf8f5ef224..ad1ab67e89c947 100644
--- a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
+++ b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
@@ -47,6 +47,8 @@
+
+