Client side logging for HTTP stress. (#41444)
authorMarie Píchová <11718369+ManickaP@users.noreply.github.com>
Fri, 28 Aug 2020 13:31:29 +0000 (15:31 +0200)
committerGitHub <noreply@github.com>
Fri, 28 Aug 2020 13:31:29 +0000 (15:31 +0200)
src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpEventListener.cs [new file with mode: 0644]
src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressClient.cs
src/libraries/System.Net.Http/tests/StressTests/HttpStress/StressServer.cs
src/libraries/System.Net.Security/tests/StressTests/SslStress/Readme.md
src/libraries/System.Net.Security/tests/StressTests/SslStress/global.json

diff --git a/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpEventListener.cs b/src/libraries/System.Net.Http/tests/StressTests/HttpStress/HttpEventListener.cs
new file mode 100644 (file)
index 0000000..d9bcc3a
--- /dev/null
@@ -0,0 +1,87 @@
+// 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.Diagnostics.Tracing;
+using System.Text;
+using System.IO;
+
+namespace HttpStress
+{
+    public sealed class LogHttpEventListener : EventListener
+    {
+        private readonly StreamWriter _log;
+
+        public LogHttpEventListener(string logPath)
+        {
+            _log = new StreamWriter(logPath, true) { AutoFlush = true };
+        }
+
+        protected override void OnEventSourceCreated(EventSource eventSource)
+        {
+            if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http")
+            {
+                EnableEvents(eventSource, EventLevel.LogAlways);
+            }
+        }
+
+        protected override void OnEventWritten(EventWrittenEventArgs eventData)
+        {
+            lock (_log)
+            {
+                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]);
+                }
+                _log.WriteLine(sb.ToString());
+            }
+        }
+
+        public override void Dispose()
+        {
+            _log.Dispose();
+            base.Dispose();
+        }
+    }
+
+    public sealed class ConsoleHttpEventListener : EventListener
+    {
+        public ConsoleHttpEventListener()
+        { }
+
+        protected override void OnEventSourceCreated(EventSource eventSource)
+        {
+            if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http")
+            {
+                EnableEvents(eventSource, EventLevel.LogAlways);
+            }
+        }
+
+        protected override void OnEventWritten(EventWrittenEventArgs eventData)
+        {
+            lock (Console.Out)
+            {
+                Console.ForegroundColor = ConsoleColor.DarkYellow;
+                Console.Write($"{eventData.TimeStamp:HH:mm:ss.fffffff}[{eventData.EventName}] ");
+                Console.ResetColor();
+                for (int i = 0; i < eventData.Payload?.Count; i++)
+                {
+                    if (i > 0)
+                    {
+                        Console.Write(", ");
+                    }
+                    Console.ForegroundColor = ConsoleColor.DarkGray;
+                    Console.Write(eventData.PayloadNames?[i] + ": ");
+                    Console.ResetColor();
+                    Console.Write(eventData.Payload[i]);
+                }
+                Console.WriteLine();
+            }
+        }
+    }
+}
index 4c87df4..142171d 100644 (file)
@@ -6,6 +6,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Concurrent;
 using System.Diagnostics;
+using System.Diagnostics.Tracing;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Net.Http;
@@ -27,6 +28,7 @@ namespace HttpStress
         private readonly Stopwatch _stopwatch = new Stopwatch();
         private readonly CancellationTokenSource _cts = new CancellationTokenSource();
         private Task? _clientTask;
+        private EventListener? _eventListener;
 
         public long TotalErrorCount => _aggregator.TotalErrorCount;
 
@@ -36,6 +38,14 @@ namespace HttpStress
             _config = configuration;
             _baseAddress = new Uri(configuration.ServerUri);
             _aggregator = new StressResultAggregator(clientOperations);
+
+            // Handle command-line arguments.
+            _eventListener =
+                configuration.LogPath == null ?
+                null :
+                (configuration.LogPath == "console" ? 
+                    (EventListener)new ConsoleHttpEventListener() :
+                    (EventListener)new LogHttpEventListener(configuration.LogPath));
         }
 
         public void Start()
@@ -78,7 +88,11 @@ namespace HttpStress
             }
         }
 
-        public void Dispose() => Stop();
+        public void Dispose()
+        {
+            Stop();
+            _eventListener?.Dispose();
+        }
 
         private async Task StartCore()
         {
@@ -208,12 +222,12 @@ namespace HttpStress
             // Representative error text of stress failure
             public string ErrorText { get; }
             // Operation id => failure timestamps
-            public Dictionary<int, List<DateTime>> Failures { get; }
+            public Dictionary<int, List<(DateTime timestamp, TimeSpan duration)>> Failures { get; }
 
             public StressFailureType(string errorText)
             {
                 ErrorText = errorText;
-                Failures = new Dictionary<int, List<DateTime>>();
+                Failures = new Dictionary<int, List<(DateTime timestamp, TimeSpan duration)>>();
             }
 
             public int FailureCount => Failures.Values.Select(x => x.Count).Sum();
@@ -279,13 +293,13 @@ namespace HttpStress
 
                     lock (failureType)
                     {
-                        if(!failureType.Failures.TryGetValue(operationIndex, out List<DateTime>? timestamps))
+                        if(!failureType.Failures.TryGetValue(operationIndex, out List<(DateTime timestamp, TimeSpan duration)>? timestamps))
                         {
-                            timestamps = new List<DateTime>();
+                            timestamps = new List<(DateTime timestamp, TimeSpan duration)>();
                             failureType.Failures.Add(operationIndex, timestamps);
                         }
 
-                        timestamps.Add(timestamp);
+                        timestamps.Add((timestamp, elapsed));
                     }
 
                     (Type exception, string message, string callSite)[] ClassifyFailure(Exception exn)
@@ -425,7 +439,7 @@ namespace HttpStress
                     Console.WriteLine(failure.ErrorText);
                     Console.WriteLine();
                     Console.ForegroundColor = ConsoleColor.Yellow;
-                    foreach (KeyValuePair<int, List<DateTime>> operation in failure.Failures)
+                    foreach (KeyValuePair<int, List<(DateTime timestamp, TimeSpan duration)>> operation in failure.Failures)
                     {
                         Console.ForegroundColor = ConsoleColor.Cyan;
                         Console.Write($"\t{_operationNames[operation.Key].PadRight(30)}");
@@ -434,7 +448,7 @@ namespace HttpStress
                         Console.Write("Fail: ");
                         Console.ResetColor();
                         Console.Write(operation.Value.Count);
-                        Console.WriteLine($"\tTimestamps: {string.Join(", ", operation.Value.Select(x => x.ToString("HH:mm:ss")))}");
+                        Console.WriteLine($"\tTimestamps: {string.Join(", ", operation.Value.Select(x => $"{x.timestamp:HH:mm:ss.fffffff} in {x.duration}"))}");
                     }
 
                     Console.ForegroundColor = ConsoleColor.Cyan;
index 6d251ba..c9401e3 100644 (file)
@@ -132,8 +132,11 @@ namespace HttpStress
 
             // Handle command-line arguments.
             _eventListener =
-                configuration.LogPath == null ? null :
-                new HttpEventListener(configuration.LogPath != "console" ? new StreamWriter(configuration.LogPath) { AutoFlush = true } : null);
+                configuration.LogPath == null ?
+                null :
+                (configuration.LogPath == "console" ? 
+                    (EventListener)new ConsoleHttpEventListener() :
+                    (EventListener)new LogHttpEventListener(configuration.LogPath));
 
             SetUpJustInTimeLogging();
 
@@ -295,7 +298,8 @@ namespace HttpStress
 
         public void Dispose()
         {
-            _webHost.Dispose(); _eventListener?.Dispose();
+            _webHost.Dispose();
+            _eventListener?.Dispose();
         }
 
         private void SetUpJustInTimeLogging()
@@ -311,7 +315,7 @@ namespace HttpStress
                         if (Console.ReadKey(intercept: true).Key == ConsoleKey.L)
                         {
                             Console.WriteLine("Enabling console event logger");
-                            _eventListener = new HttpEventListener();
+                            _eventListener = new ConsoleHttpEventListener();
                             break;
                         }
                     }
@@ -342,54 +346,6 @@ namespace HttpStress
             }
         }
 
-        /// <summary>EventListener that dumps HTTP events out to either the console or a stream writer.</summary>
-        private sealed class HttpEventListener : EventListener
-        {
-            private readonly StreamWriter? _writer;
-
-            public HttpEventListener(StreamWriter? writer = null) => _writer = writer;
-
-            protected override void OnEventSourceCreated(EventSource eventSource)
-            {
-                if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http")
-                    EnableEvents(eventSource, EventLevel.LogAlways);
-            }
-
-            protected override void OnEventWritten(EventWrittenEventArgs eventData)
-            {
-                lock (Console.Out)
-                {
-                    if (_writer != null)
-                    {
-                        var sb = new StringBuilder().Append($"[{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]);
-                        }
-                        _writer.WriteLine(sb);
-                    }
-                    else
-                    {
-                        Console.ForegroundColor = ConsoleColor.DarkYellow;
-                        Console.Write($"[{eventData.EventName}] ");
-                        Console.ResetColor();
-                        for (int i = 0; i < eventData.Payload?.Count; i++)
-                        {
-                            if (i > 0)
-                                Console.Write(", ");
-                            Console.ForegroundColor = ConsoleColor.DarkGray;
-                            Console.Write(eventData.PayloadNames?[i] + ": ");
-                            Console.ResetColor();
-                            Console.Write(eventData.Payload[i]);
-                        }
-                        Console.WriteLine();
-                    }
-                }
-            }
-        }
-
         private static string CreateResponseContent(HttpContext ctx)
         {
             return ServerContentUtils.CreateStringContent(GetExpectedContentLength());
index 06a4b53..ee46422 100644 (file)
@@ -24,7 +24,7 @@ To achieve this, we will first need to build a new sdk from source. This can be
 
 ### Running using docker-compose
 
-The prefered way of running the stress suite is using docker-compose,
+The preferred way of running the stress suite is using docker-compose,
 which can be used to target both linux and windows containers.
 Docker and compose-compose are required for this step (both included in [docker for windows](https://docs.docker.com/docker-for-windows/)).