public static async Task Main(string[] args) { if (args.Length == 0) { args = new[] { "./.env" }; } if (args.Length == 1) { args = ArgumentFileReader.Read(args[0])?.ToArray() ?? Array.Empty <string>(); } var runArgs = ParseAndPromptForArguments(args); var runDuration = DefaultRunDuration; var errorLogPath = DefaultErrorLogPath; // If not provided or malformed, use the default. if ((!string.IsNullOrEmpty(runArgs.RunDurationHours)) && (int.TryParse(runArgs.RunDurationHours, out var hours))) { runDuration = TimeSpan.FromHours(hours); } if (!string.IsNullOrEmpty(runArgs.LogPath)) { errorLogPath = runArgs.LogPath; } using var cancellationSource = new CancellationTokenSource(); using var errorWriter = new StreamWriter(File.Open(errorLogPath, FileMode.Create, FileAccess.Write, FileShare.Read)); using var metricsWriter = Console.Out; using var azureEventListener = ListenForEventSourceErrors(errorWriter); try { var message = $"{ Environment.NewLine }{ Environment.NewLine }=============================={ Environment.NewLine } Run Starting{ Environment.NewLine }=============================={ Environment.NewLine }"; metricsWriter.WriteLine(message); errorWriter.WriteLine(message); cancellationSource.CancelAfter(runDuration); var configuration = new TestConfiguration { EventHubsConnectionString = runArgs.EventHubsConnectionString, EventHub = runArgs.EventHub }; var testRun = new TestRun(configuration); var testRunTask = testRun.Start(cancellationSource.Token); // Make an initial metrics report now that the run is taking place. await(Task.Delay(TimeSpan.FromSeconds(1))); await ReportMetricsAsync(metricsWriter, testRun.Metrics, runDuration, configuration); // Allow the run to take place, periodically reporting. while (!cancellationSource.IsCancellationRequested) { try { await Task.Delay(DefaultProcessReportInterval, cancellationSource.Token); } catch (TaskCanceledException) { message = $"{ Environment.NewLine }{ Environment.NewLine }--------------------------------------------------------------------------{ Environment.NewLine } The run is ending. Waiting for clean-up and final reporting...{ Environment.NewLine }--------------------------------------------------------------------------{ Environment.NewLine }"; metricsWriter.WriteLine(message); errorWriter.WriteLine(message); } await Task.WhenAll ( ReportMetricsAsync(metricsWriter, testRun.Metrics, runDuration, configuration), ReportErrorsAsync(errorWriter, testRun.ErrorsObserved) ); } // Allow the run to complete and then perform a final pas on reporting // to ensure that any straggling operations are captures. await testRunTask; Interlocked.Exchange(ref testRun.Metrics.RunDurationMilliseconds, runDuration.TotalMilliseconds); await Task.WhenAll ( ReportMetricsAsync(metricsWriter, testRun.Metrics, runDuration, configuration), ReportErrorsAsync(errorWriter, testRun.ErrorsObserved) ); message = $"{ Environment.NewLine }{ Environment.NewLine }=============================={ Environment.NewLine } Run Complete{ Environment.NewLine }=============================="; metricsWriter.WriteLine(message); errorWriter.WriteLine(message); } catch (Exception ex) when (ex is OutOfMemoryException || ex is StackOverflowException || ex is ThreadAbortException) { Environment.FailFast(ex.Message); } catch (Exception ex) { var message = $"{ Environment.NewLine }{ Environment.NewLine }=============================={ Environment.NewLine } Error in the main loop. Run aborting. Message: [{ ex.Message }]{ Environment.NewLine }=============================="; metricsWriter.WriteLine(message); errorWriter.WriteLine(message); } finally { errorWriter.Close(); } }
private static Task ReportMetricsAsync(TextWriter writer, Metrics metrics, TimeSpan runDuration, TestConfiguration configuration) { long metric; var message = new StringBuilder(); // Run time var runDurationMilliseconds = Interlocked.CompareExchange(ref metrics.RunDurationMilliseconds, 0.0, 0.0); var currentDuration = TimeSpan.FromMilliseconds(runDurationMilliseconds > 0.0 ? runDurationMilliseconds : 1); var averageMemory = metrics.TotalMemoryUsed / metrics.MemorySamples; message.AppendLine("Run Metrics"); message.AppendLine("========================="); message.AppendLine($"\tRun Duration:\t\t\t{ runDuration.ToString(@"dd\.hh\:mm\:ss") }"); message.AppendLine($"\tElapsed:\t\t\t{ currentDuration.ToString(@"dd\.hh\:mm\:ss") } ({ (currentDuration / runDuration).ToString("P", CultureInfo.InvariantCulture) })"); message.AppendLine($"\tTotal Processor Time:\t\t{ metrics.TotalProcessorTime.ToString(@"dd\.hh\:mm\:ss") }"); message.AppendLine($"\tAverage Memory Use:\t\t{ FormatBytes(averageMemory) }"); message.AppendLine($"\tCurrent Memory Use:\t\t{ FormatBytes(metrics.MemoryUsed) }"); message.AppendLine($"\tPeak Memory Use:\t\t{ FormatBytes(metrics.PeakPhysicalMemory) }"); message.AppendLine($"\tGC Gen 0 Collections:\t\t{ metrics.GenerationZeroCollections.ToString("n0") }"); message.AppendLine($"\tGC Gen 1 Collections:\t\t{ metrics.GenerationOneCollections.ToString("n0") }"); message.AppendLine($"\tGC Gen 2 Collections:\t\t{ metrics.GenerationTwoCollections.ToString("n0") }"); message.AppendLine($"\tNumber of Publishers:\t\t{ configuration.ProducerCount.ToString("n0") }"); message.AppendLine($"\tConcurrent Sends per Publisher:\t{ configuration.ConcurrentSends.ToString("n0") }"); message.AppendLine($"\tTarget Batch Size:\t\t{ configuration.PublishBatchSize.ToString("n0") } (events)"); message.AppendLine($"\tMinimum Message Size:\t\t{ configuration.PublishingBodyMinBytes.ToString("n0") } (bytes)"); message.AppendLine($"\tLarge Message Factor:\t\t{ configuration.LargeMessageRandomFactorPercent.ToString("P", CultureInfo.InvariantCulture) }"); message.AppendLine(); // Publish and read pairing message.AppendLine("Publishing"); message.AppendLine("========================="); var publishAttempts = (double)Interlocked.Read(ref metrics.PublishAttempts); message.AppendLine($"\tSend Attempts:\t\t\t{ publishAttempts.ToString("n0") }"); publishAttempts = (publishAttempts > 0) ? publishAttempts : 0.001; var batches = (double)Interlocked.Read(ref metrics.BatchesPublished); message.AppendLine($"\tBatches Published:\t\t{ batches.ToString("n0") } ({ (batches / publishAttempts).ToString("P", CultureInfo.InvariantCulture) })"); batches = (batches > 0) ? batches : 0.001; metric = Interlocked.Read(ref metrics.EventsPublished); message.AppendLine($"\tEvents Published:\t\t{ metric.ToString("n0") }"); message.AppendLine($"\tAverage Events per Batch:\t{ ( metric / batches).ToString("n0") }"); metric = Interlocked.Read(ref metrics.TotalPublshedSizeBytes); message.AppendLine($"\tAverage Batch Size:\t\t{ FormatBytes((long)(metric / batches)) }"); message.AppendLine(); // Client health message.AppendLine("Client Health"); message.AppendLine("========================="); metric = Interlocked.Read(ref metrics.ProducerRestarted); message.AppendLine($"\tProducer Restarts:\t\t{ metric }"); var cancelledSends = Interlocked.Read(ref metrics.CanceledSendExceptions); message.AppendLine($"\tAborted Sends:\t\t\t{ cancelledSends }"); message.AppendLine(); // Exceptions message.AppendLine("Exception Breakdown"); message.AppendLine("========================="); var totalExceptions = (double)Interlocked.Read(ref metrics.TotalExceptions); message.AppendLine($"\tExceptions for All Operations:\t{ totalExceptions.ToString("n0") } ({ (totalExceptions / publishAttempts).ToString("P", CultureInfo.InvariantCulture) })"); totalExceptions = (totalExceptions > 0) ? totalExceptions : 0.001; metric = Interlocked.Read(ref metrics.SendExceptions); message.AppendLine($"\tException During Send:\t\t{ metric.ToString("n0") } ({ (metric / totalExceptions).ToString("P", CultureInfo.InvariantCulture) })"); metric = Interlocked.Read(ref metrics.GeneralExceptions); message.AppendLine($"\tGeneral Exceptions:\t\t{ metric.ToString("n0") } ({ (metric / totalExceptions).ToString("P", CultureInfo.InvariantCulture) })"); metric = Interlocked.Read(ref metrics.TimeoutExceptions); message.AppendLine($"\tTimeout Exceptions:\t\t{ metric.ToString("n0") } ({ (metric / totalExceptions).ToString("P", CultureInfo.InvariantCulture) })"); message.AppendLine($"\tOperation Canceled Exceptions:\t{ cancelledSends.ToString("n0") } ({ (cancelledSends / totalExceptions).ToString("P", CultureInfo.InvariantCulture) })"); metric = Interlocked.Read(ref metrics.CommunicationExceptions); message.AppendLine($"\tCommunication Exceptions:\t{ metric.ToString("n0") } ({ (metric / totalExceptions).ToString("P", CultureInfo.InvariantCulture) })"); metric = Interlocked.Read(ref metrics.ServiceBusyExceptions); message.AppendLine($"\tService Busy Exceptions:\t{ metric.ToString("n0") } ({ (metric / totalExceptions).ToString("P", CultureInfo.InvariantCulture) })"); // Spacing message.AppendLine(); message.AppendLine(); message.AppendLine(); message.AppendLine(); return(writer.WriteLineAsync(message.ToString())); }