// 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 { } } }