// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Diagnostics.Tracing;
using System.Text;
namespace ZymonicServices;
///
/// 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 ZymonicEventListener(logger, ZymonicEventListener.NetworkingEvents);
///
public sealed class ZymonicEventListener : 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.Primitives",
"Private.InternalDiagnostics.System.Net.Requests",
};
private IZymonicLogger _logger;
private readonly HashSet _sourceNames;
// Until https://github.com/dotnet/runtime/issues/63979 is solved.
private List _eventSources = new List();
public ZymonicEventListener(IZymonicLogger logger, params string[] sourceNames)
{
List eventSources = _eventSources;
lock (this)
{
_logger = logger;
_sourceNames = new HashSet(sourceNames);
// Commented out line below as it generates a warning
//_eventSources = null;
}
// eventSources were populated in the base ctor and are now owned by this thread, enable them now.
foreach (EventSource eventSource in eventSources)
{
if (_sourceNames.Contains(eventSource.Name))
{
EnableEvents(eventSource, EventLevel.LogAlways);
}
}
}
protected override void OnEventSourceCreated(EventSource eventSource)
{
// We're likely called from base ctor, if so, just save the event source for later initialization.
if (_sourceNames is null)
{
lock (this)
{
if (_sourceNames is null)
{
_eventSources.Add(eventSource);
return;
}
}
}
// Second pass called after our ctor, allow logging for specified source names.
if (_sourceNames.Contains(eventSource.Name))
{
EnableEvents(eventSource, EventLevel.LogAlways);
}
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
ArrayList paramObjs = new ArrayList();
StringBuilder sb = new StringBuilder().
#if NET || NETSTANDARD2_1_OR_GREATER
Append("{TimeStamp}[{EventName}] ");
paramObjs.Add(eventData.TimeStamp);
paramObjs.Add(eventData.EventName);
#else
Append("[{EventName}] ");
paramObjs.Add(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.PayloadNames?[i]).Append("}");
paramObjs.Add(eventData.Payload[i]);
}
try
{
_logger.LogTrace(sb.ToString(), paramObjs.ToArray()!);
}
catch { }
}
}