diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e779c4c..da9467d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - 0.1.8 + 0.1.9 Tony Redondo, Grégory Léocadie net6.0;net7.0;net8.0 enable diff --git a/src/TimeItSharp.Common/Configuration/Builder/ConfigBuilder.cs b/src/TimeItSharp.Common/Configuration/Builder/ConfigBuilder.cs index 19255a4..f135461 100644 --- a/src/TimeItSharp.Common/Configuration/Builder/ConfigBuilder.cs +++ b/src/TimeItSharp.Common/Configuration/Builder/ConfigBuilder.cs @@ -86,17 +86,6 @@ public ConfigBuilder WithDatadog(bool enabled) return this; } - /// - /// Sets if the metrics importer should be enabled or not - /// - /// True if the metrics importer is enabled; false if disabled - /// Configuration builder instance - public ConfigBuilder WithMetrics(bool enabled) - { - _configuration.EnableMetrics = enabled; - return this; - } - #endregion #region Scenarios @@ -134,6 +123,28 @@ public ConfigBuilder WithScenario(Func scenari #endregion + /// + /// Sets if the runtime metrics importer should be enabled or not + /// + /// True if the metrics importer is enabled; false if disabled + /// Configuration builder instance + public ConfigBuilder WithMetrics(bool enabled) + { + _configuration.EnableMetrics = enabled; + return this; + } + + /// + /// Sets the process name to collect runtime metrics + /// + /// Process name + /// Configuration builder instance + public ConfigBuilder WithMetricsProcessName(string processName) + { + _configuration.MetricsProcessName = processName; + return this; + } + /// /// Sets the name of the configuration /// @@ -141,7 +152,7 @@ public ConfigBuilder WithScenario(Func scenari /// Configuration builder instance public ConfigBuilder WithName(string name) { - _configuration.FileName = name; + _configuration.Name = name; return this; } @@ -180,6 +191,17 @@ public ConfigBuilder ClearExporters() /// Configuration builder instance public ConfigBuilder WithExporter(AssemblyLoadInfo exporter) { + // Check if exporter is already there. + foreach (var existingExporter in _configuration.Exporters) + { + if (existingExporter.Name == exporter.Name && + existingExporter.FilePath == exporter.FilePath && + existingExporter.Type == exporter.Type) + { + return this; + } + } + _configuration.Exporters.Add(exporter); return this; } @@ -202,6 +224,15 @@ public ConfigBuilder WithExporter(params AssemblyLoadInfo[] exporters) /// Configuration builder instance public ConfigBuilder WithExporter(string exporterName) { + // Check if exporter is already there. + foreach (var exporter in _configuration.Exporters) + { + if (exporter.Name == exporterName) + { + return this; + } + } + _configuration.Exporters.Add(new AssemblyLoadInfo { Name = exporterName @@ -228,6 +259,16 @@ public ConfigBuilder WithExporter() /// Configuration builder instance public ConfigBuilder WithExporter(Type exporterType) { + // Check if exporter is already there. + foreach (var exporter in _configuration.Exporters) + { + if (exporter.FilePath == exporterType.Assembly.Location && + exporter.Type == exporterType.FullName) + { + return this; + } + } + _configuration.Exporters.Add(new AssemblyLoadInfo { FilePath = exporterType.Assembly.Location, @@ -291,6 +332,17 @@ public ConfigBuilder ClearAssertors() /// Configuration builder instance public ConfigBuilder WithAssertor(AssemblyLoadInfo assertor) { + // Check if assertor is already there. + foreach (var existing in _configuration.Assertors) + { + if (existing.Name == assertor.Name && + existing.FilePath == assertor.FilePath && + existing.Type == assertor.Type) + { + return this; + } + } + _configuration.Assertors.Add(assertor); return this; } @@ -313,6 +365,15 @@ public ConfigBuilder WithAssertor(params AssemblyLoadInfo[] assertors) /// Configuration builder instance public ConfigBuilder WithAssertor(string assertorName) { + // Check if assertors is already there. + foreach (var exiting in _configuration.Assertors) + { + if (exiting.Name == assertorName) + { + return this; + } + } + _configuration.Assertors.Add(new AssemblyLoadInfo { Name = assertorName @@ -339,6 +400,16 @@ public ConfigBuilder WithAssertor() /// Configuration builder instance public ConfigBuilder WithAssertor(Type assertorType) { + // Check if assertors is already there. + foreach (var existing in _configuration.Assertors) + { + if (existing.FilePath == assertorType.Assembly.Location && + existing.Type == assertorType.FullName) + { + return this; + } + } + _configuration.Assertors.Add(new AssemblyLoadInfo { FilePath = assertorType.Assembly.Location, @@ -397,6 +468,17 @@ public ConfigBuilder ClearServices() /// Configuration builder instance public ConfigBuilder WithService(AssemblyLoadInfo service) { + // Check if service is already there. + foreach (var existing in _configuration.Services) + { + if (existing.Name == service.Name && + existing.FilePath == service.FilePath && + existing.Type == service.Type) + { + return this; + } + } + _configuration.Services.Add(service); return this; } @@ -419,6 +501,15 @@ public ConfigBuilder WithService(params AssemblyLoadInfo[] services) /// Configuration builder instance public ConfigBuilder WithService(string serviceName) { + // Check if service is already there. + foreach (var exiting in _configuration.Services) + { + if (exiting.Name == serviceName) + { + return this; + } + } + _configuration.Services.Add(new AssemblyLoadInfo { Name = serviceName @@ -445,6 +536,16 @@ public ConfigBuilder WithService() /// Configuration builder instance public ConfigBuilder WithService(Type serviceType) { + // Check if services is already there. + foreach (var existing in _configuration.Services) + { + if (existing.FilePath == serviceType.Assembly.Location && + existing.Type == serviceType.FullName) + { + return this; + } + } + _configuration.Services.Add(new AssemblyLoadInfo { FilePath = serviceType.Assembly.Location, diff --git a/src/TimeItSharp.Common/Configuration/Config.cs b/src/TimeItSharp.Common/Configuration/Config.cs index cf47c47..50503be 100644 --- a/src/TimeItSharp.Common/Configuration/Config.cs +++ b/src/TimeItSharp.Common/Configuration/Config.cs @@ -13,6 +13,9 @@ public class Config : ProcessData [JsonIgnore] public string FileName { get; set; } + + [JsonIgnore] + public string Name { get; set; } [JsonPropertyName("warmUpCount")] public int WarmUpCount { get; set; } @@ -25,6 +28,9 @@ public class Config : ProcessData [JsonPropertyName("enableMetrics")] public bool EnableMetrics { get; set; } + + [JsonPropertyName("metricsProcessName")] + public string MetricsProcessName { get; set; } [JsonPropertyName("scenarios")] public List Scenarios { get; set; } @@ -46,10 +52,12 @@ public Config() FilePath = string.Empty; Path = string.Empty; FileName = string.Empty; + Name = string.Empty; WarmUpCount = 0; Count = 0; EnableDatadog = false; EnableMetrics = true; + MetricsProcessName = string.Empty; Scenarios = new(); JsonExporterFilePath = string.Empty; Exporters = new(); @@ -92,10 +100,12 @@ public static Config LoadConfiguration(string filePath) FilePath = FilePath, Path = Path, FileName = FileName, + Name = Name, WarmUpCount = WarmUpCount, Count = Count, EnableDatadog = EnableDatadog, EnableMetrics = EnableMetrics, + MetricsProcessName = MetricsProcessName, Scenarios = Scenarios.Select(i => i.Clone()).ToList(), JsonExporterFilePath = JsonExporterFilePath, Exporters = Exporters.Select(i => i.Clone()).ToList(), diff --git a/src/TimeItSharp.Common/Exporters/ConsoleExporter.cs b/src/TimeItSharp.Common/Exporters/ConsoleExporter.cs index a04d781..378b4cf 100644 --- a/src/TimeItSharp.Common/Exporters/ConsoleExporter.cs +++ b/src/TimeItSharp.Common/Exporters/ConsoleExporter.cs @@ -80,6 +80,28 @@ public void Export(TimeitResult results) // Write table AnsiConsole.Write(outliersTable); + var resultsList = results.Scenarios.ToList(); + + // Show distribution of results + if (_options.Configuration.Count >= 10) + { + AnsiConsole.MarkupLine("[aqua bold underline]### Distribution:[/]"); + AnsiConsole.WriteLine(); + for (var idx = 0; idx < resultsList.Count; idx++) + { + var result = resultsList[idx]; + AnsiConsole.MarkupLine($"[dodgerblue1 bold]{result.Scenario?.Name}:[/]"); + if (result.IsBimodal) + { + AnsiConsole.MarkupLine("[yellow bold]Scenario '{0}' is Bimodal.[/] Peak count: {1}", result.Name, + result.PeakCount); + } + + WriteHistogramHorizontal(result.Histogram, result.HistogramLabels); + AnsiConsole.WriteLine(); + } + } + // **************************************** // Summary table AnsiConsole.MarkupLine("[aqua bold underline]### Summary:[/]"); @@ -118,7 +140,6 @@ public void Export(TimeitResult results) summaryTable.AddColumns(columnList.ToArray()); // Add rows - var resultsList = results.Scenarios.ToList(); for (var idx = 0; idx < resultsList.Count; idx++) { var result = resultsList[idx]; @@ -137,7 +158,7 @@ public void Export(TimeitResult results) $"[aqua]{Math.Round(Utils.FromNanosecondsToMilliseconds(result.Max), 6)}ms[/]", $"[aqua]{Math.Round(Utils.FromNanosecondsToMilliseconds(result.P95), 6)}ms[/]", $"[aqua]{Math.Round(Utils.FromNanosecondsToMilliseconds(result.P90), 6)}ms[/]", - $"[aqua]{result.Outliers.Count}[/]" + $"[aqua]{result.Outliers.Count} {{{Math.Round(result.OutliersThreshold, 2)}}}[/]" }; foreach (var additionalMetric in additionalMetrics) @@ -207,7 +228,7 @@ public void Export(TimeitResult results) $"{Math.Round(Utils.FromNanosecondsToMilliseconds(result.Max), 6)}ms", $"{Math.Round(Utils.FromNanosecondsToMilliseconds(result.P95), 6)}ms", $"{Math.Round(Utils.FromNanosecondsToMilliseconds(result.P90), 6)}ms", - $"{result.Outliers.Count}" + $"{result.Outliers.Count} {{{Math.Round(result.OutliersThreshold, 2)}}}" }; foreach (var additionalMetric in additionalMetrics) @@ -228,55 +249,46 @@ public void Export(TimeitResult results) // ****************************** // Write overhead table - AnsiConsole.MarkupLine("[aqua bold underline]### Overheads:[/]"); - var overheadTable = new Table() - .MarkdownBorder(); - - var lstOverheadColumns = new List(); - lstOverheadColumns.Add(string.Empty); - foreach (var scenario in results.Scenarios) + if (results.Scenarios.Count > 1) { - lstOverheadColumns.Add($"[dodgerblue1 bold]{scenario.Name}[/]"); - } + AnsiConsole.MarkupLine("[aqua bold underline]### Overheads:[/]"); + var overheadTable = new Table() + .MarkdownBorder(); - overheadTable.AddColumns(lstOverheadColumns.ToArray()); - for (var i = 0; i < results.Scenarios.Count; i++) - { - var row = new List + var lstOverheadColumns = new List(); + lstOverheadColumns.Add(string.Empty); + foreach (var scenario in results.Scenarios) { - $"[dodgerblue1 bold]{results.Scenarios[i].Name}[/]" - }; + lstOverheadColumns.Add($"[dodgerblue1 bold]{scenario.Name}[/]"); + } - for (var j = 0; j < results.Scenarios.Count; j++) + overheadTable.AddColumns(lstOverheadColumns.ToArray()); + for (var i = 0; i < results.Scenarios.Count; i++) { - var value = results.Overheads?[i][j] ?? 0; - if (value == 0) + var row = new List { - row.Add("--"); - } - else + $"[dodgerblue1 bold]{results.Scenarios[i].Name}[/]" + }; + + for (var j = 0; j < results.Scenarios.Count; j++) { - row.Add($"{value.ToString(CultureInfo.InvariantCulture)}%"); + var value = results.Overheads?[i][j] ?? 0; + if (value == 0) + { + row.Add("--"); + } + else + { + row.Add($"{value.ToString(CultureInfo.InvariantCulture)}%"); + } } - } - - overheadTable.AddRow(row.ToArray()); - } - - // Write overhead table - AnsiConsole.Write(overheadTable); - AnsiConsole.WriteLine(); - // Check if is bimodal - for (var idx = 0; idx < resultsList.Count; idx++) - { - var result = resultsList[idx]; - if (result.IsBimodal) - { - AnsiConsole.MarkupLine("[yellow bold]Scenario '{0}' is Bimodal.[/] Peak count: {1}", result.Name, result.PeakCount); - WriteHistogramHorizontal(result.Histogram, result.HistogramLabels); - AnsiConsole.WriteLine(); + overheadTable.AddRow(row.ToArray()); } + + // Write overhead table + AnsiConsole.Write(overheadTable); + AnsiConsole.WriteLine(); } // Write Errors diff --git a/src/TimeItSharp.Common/Exporters/DatadogExporter.cs b/src/TimeItSharp.Common/Exporters/DatadogExporter.cs index d15d6f7..5abb6fb 100644 --- a/src/TimeItSharp.Common/Exporters/DatadogExporter.cs +++ b/src/TimeItSharp.Common/Exporters/DatadogExporter.cs @@ -9,6 +9,7 @@ namespace TimeItSharp.Common.Exporters; public sealed class DatadogExporter : IExporter { + private string? _configName; private InitOptions _options; private readonly TestSession _testSession; private readonly DateTime _startDate; @@ -30,7 +31,8 @@ public DatadogExporter() public void Initialize(InitOptions options) { _options = options; - _testModule ??= _testSession.CreateModule(options.Configuration?.FileName ?? "config_file", "time-it", typeof(DatadogExporter).Assembly.GetName().Version?.ToString() ?? "(unknown)", _startDate); + _configName = options.Configuration?.Name ?? options.Configuration?.FileName; + _testModule ??= _testSession.CreateModule(_configName ?? "config_file", "time-it", typeof(DatadogExporter).Assembly.GetName().Version?.ToString() ?? "(unknown)", _startDate); } /// @@ -38,8 +40,8 @@ public void Export(TimeitResult results) { var errors = false; var minStartDate = results.Scenarios.Select(r => r.Start).Min(); - _testModule ??= _testSession.CreateModule(_options.Configuration?.FileName ?? "config_file", "time-it", typeof(DatadogExporter).Assembly.GetName().Version?.ToString() ?? "(unknown)", minStartDate); - var testSuite = _testModule.GetOrCreateSuite("scenarios", minStartDate); + _testModule ??= _testSession.CreateModule(_configName ?? "config_file", "time-it", typeof(DatadogExporter).Assembly.GetName().Version?.ToString() ?? "(unknown)", minStartDate); + var testSuite = _testModule.GetOrCreateSuite(_configName is not null ? $"{_configName}.scenarios" : "scenarios", minStartDate); try { for (var i = 0; i < results.Scenarios.Count; i++) @@ -65,9 +67,11 @@ public void Export(TimeitResult results) "Duration of a run", BenchmarkDiscreteStats.GetFrom(scenarioResult.Durations.ToArray())); - // Report if is bimodal + // Report benchmark duration data test.SetTag("benchmark.duration.bimodal", scenarioResult.IsBimodal ? "true": "false"); test.SetTag("benchmark.duration.peakcount", scenarioResult.PeakCount); + test.SetTag("benchmark.duration.outliers_threshold", Math.Round(scenarioResult.OutliersThreshold, 2)); + test.SetTag("benchmark.duration.outliers_count", scenarioResult.Outliers?.Count ?? 0); // Add metrics if (scenarioResult.MetricsData.TryGetValue("process.time_to_start_ms", out var timeToStart)) diff --git a/src/TimeItSharp.Common/Results/ScenarioResult.cs b/src/TimeItSharp.Common/Results/ScenarioResult.cs index 5982570..df83d68 100644 --- a/src/TimeItSharp.Common/Results/ScenarioResult.cs +++ b/src/TimeItSharp.Common/Results/ScenarioResult.cs @@ -86,6 +86,9 @@ public sealed class ScenarioResult : Scenario [JsonPropertyName("status")] [JsonConverter(typeof(JsonStringEnumConverter))] public Status Status { get; set; } + + [JsonPropertyName("outliersThreshold")] + public double OutliersThreshold { get; set; } public ScenarioResult() { diff --git a/src/TimeItSharp.Common/ScenarioProcessor.cs b/src/TimeItSharp.Common/ScenarioProcessor.cs index 1396b0c..b5f4d1b 100644 --- a/src/TimeItSharp.Common/ScenarioProcessor.cs +++ b/src/TimeItSharp.Common/ScenarioProcessor.cs @@ -121,6 +121,12 @@ public void PrepareScenario(Scenario scenario) { scenario.EnvironmentVariables[Constants.StartupHookEnvironmentVariable] = startupHookLocation; } + + if (!string.IsNullOrEmpty(_configuration.MetricsProcessName)) + { + scenario.EnvironmentVariables[Constants.TimeItMetricsProcessName] = + _configuration.MetricsProcessName; + } } else { @@ -246,30 +252,24 @@ public void CleanScenario(Scenario scenario) // Get outliers List newDurations = new List(); List outliers = new List(); - var threshold = 2.0d; + var threshold = 0.5d; var peakCount = 0; int[] histogram = Array.Empty(); Range[] labels = Array.Empty>(); var isBimodal = false; - while (threshold > 0) + while (threshold < 2.0d) { newDurations = Utils.RemoveOutliers(durations, threshold).ToList(); outliers = durations.Where(d => !newDurations.Contains(d)).ToList(); - isBimodal = Utils.IsBimodal(CollectionsMarshal.AsSpan(newDurations), out peakCount, out histogram, out labels, _configuration.Count / 10); - if (!isBimodal) - { - // if the series is no longer bimodal we don't remove more outliers - break; - } - + isBimodal = Utils.IsBimodal(CollectionsMarshal.AsSpan(newDurations), out peakCount, out histogram, out labels, Math.Min(10, Math.Max(_configuration.Count / 10, 3))); var outliersPercent = ((double)outliers.Count / durations.Count) * 100; - if (outliersPercent >= 20) + if (outliersPercent < 20 && !isBimodal) { // outliers must be not more than 20% of the data break; } - threshold -= 0.1; + threshold += 0.1; } var mean = newDurations.Mean(); @@ -349,6 +349,7 @@ public void CleanScenario(Scenario scenario) Timeout = scenario.Timeout, Tags = scenario.Tags, Status = assertResponse.Status, + OutliersThreshold = threshold, }; _callbacksTriggers.ScenarioFinish(scenarioResult); diff --git a/src/TimeItSharp.StartupHook/Constants.cs b/src/TimeItSharp.StartupHook/Constants.cs index 8a14abd..90df1a5 100644 --- a/src/TimeItSharp.StartupHook/Constants.cs +++ b/src/TimeItSharp.StartupHook/Constants.cs @@ -4,6 +4,7 @@ internal static class Constants { public const string StartupHookEnvironmentVariable = "DOTNET_STARTUP_HOOKS"; public const string TimeItMetricsTemporalPathEnvironmentVariable = "TIMEIT_METRICS_TEMPORAL_PATH"; + public const string TimeItMetricsProcessName = "TIMEIT_METRICS_PROCESS_NAME"; public const string ProcessTimeToStartMetricName = "process.time_to_start_ms"; public const string ProcessTimeToMainMetricName = "process.time_to_main_ms"; public const string ProcessTimeToEndMetricName = "process.time_to_end_ms"; diff --git a/src/TimeItSharp.StartupHook/StartupHook.cs b/src/TimeItSharp.StartupHook/StartupHook.cs index 4f46984..c47a90d 100644 --- a/src/TimeItSharp.StartupHook/StartupHook.cs +++ b/src/TimeItSharp.StartupHook/StartupHook.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using TimeItSharp; using TimeItSharp.RuntimeMetrics; @@ -9,8 +10,25 @@ public sealed class StartupHook public static void Initialize() { var startDate = Clock.UtcNow; - if (Environment.GetEnvironmentVariable(Constants.TimeItMetricsTemporalPathEnvironmentVariable) is - { Length: > 0 } metricsPath) + var metricsPath = Environment.GetEnvironmentVariable(Constants.TimeItMetricsTemporalPathEnvironmentVariable); + if (string.IsNullOrEmpty(metricsPath)) + { + return; + } + + bool enableMetrics; + var processName = Environment.GetEnvironmentVariable(Constants.TimeItMetricsProcessName); + if (string.IsNullOrEmpty(processName)) + { + enableMetrics = true; + } + else + { + var currentProcessName = Process.GetCurrentProcess().ProcessName; + enableMetrics = string.Equals(currentProcessName, processName, StringComparison.OrdinalIgnoreCase); + } + + if (enableMetrics) { _fileStatsd = new FileStorage(metricsPath); _fileStatsd.Gauge(Constants.ProcessStartTimeUtcMetricName, startDate.ToBinary()); diff --git a/src/TimeItSharp/Program.cs b/src/TimeItSharp/Program.cs index ee7b32a..2c910e1 100644 --- a/src/TimeItSharp/Program.cs +++ b/src/TimeItSharp/Program.cs @@ -1,11 +1,15 @@ using Spectre.Console; using TimeItSharp.Common; using System.CommandLine; +using System.Text.Json; +using TimeItSharp.Common.Configuration; +using TimeItSharp.Common.Configuration.Builder; +using TimeItSharp.Common.Exporters; var version = typeof(Program).Assembly.GetName().Version!; AnsiConsole.MarkupLine("[bold dodgerblue1 underline]TimeItSharp v{0}[/]", $"{version.Major}.{version.Minor}.{version.Build}"); -var argument = new Argument("configuration file", "The JSON configuration file"); +var argument = new Argument("configuration file or process name", "The JSON configuration file or process name"); var templateVariables = new Option( "--variable", isDefault: true, @@ -42,20 +46,99 @@ return tvs; }) { Arity = ArgumentArity.OneOrMore }; +var count = new Option("--count", "Number of iterations to run"); +var warmup = new Option("--warmup", "Number of iterations to warm up"); +var jsonExporter = new Option("--json-exporter", () => false, "Enable JSON exporter"); +var datadogExporter = new Option("--datadog-exporter", () => false, "Enable Datadog exporter"); var root = new RootCommand { argument, - templateVariables + templateVariables, + count, + warmup, + jsonExporter, + datadogExporter }; -root.SetHandler(async (configFile, templateVariables) => +root.SetHandler(async (configFile, templateVariables, countValue, warmupValue, jsonExporterValue, datadogExporterValue) => { - var exitCode = await TimeItEngine.RunAsync(configFile, new TimeItOptions(templateVariables)).ConfigureAwait(false); + var isConfigFile = false; + if (File.Exists(configFile)) + { + try + { + await using var fstream = File.Open(configFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + var config = JsonSerializer.Deserialize(fstream); + isConfigFile = config is not null; + } + catch + { + // . + } + } + else + { + AnsiConsole.MarkupLine("Configuration file not found, trying to run as a process name..."); + } + + var exitCode = 0; + if (isConfigFile) + { + var config = Config.LoadConfiguration(configFile); + config.WarmUpCount = warmupValue ?? config.WarmUpCount; + config.Count = countValue ?? config.Count; + var configBuilder = new ConfigBuilder(config); + if (jsonExporterValue) + { + configBuilder.WithExporter(); + } + + if (datadogExporterValue) + { + configBuilder.WithExporter(); + } + + exitCode = await TimeItEngine.RunAsync(configBuilder, new TimeItOptions(templateVariables)).ConfigureAwait(false); + } + else + { + var commandLineArray = configFile.Split(' ', StringSplitOptions.None); + var processName = commandLineArray[0]; + var processArgs = string.Empty; + if (commandLineArray.Length > 1) + { + processArgs = string.Join(' ', commandLineArray.Skip(1)); + } + + var configBuilder = ConfigBuilder.Create() + .WithName(configFile) + .WithProcessName(processName) + .WithProcessArguments(processArgs) + .WithMetrics(true) + .WithWarmupCount(warmupValue ?? 1) + .WithCount(countValue ?? 10) + .WithExporter() + .WithTimeout(t => t.WithMaxDuration((int)TimeSpan.FromMinutes(30).TotalSeconds)) + .WithScenario(s => s.WithName("Default")); + + if (jsonExporterValue) + { + configBuilder.WithExporter(); + } + + if (datadogExporterValue) + { + configBuilder.WithExporter(); + } + + exitCode = await TimeItEngine.RunAsync(configBuilder, new TimeItOptions(templateVariables)).ConfigureAwait(false); + } + if (exitCode != 0) { Environment.Exit(exitCode); } -}, argument, templateVariables); +}, argument, templateVariables, count, warmup, jsonExporter, datadogExporter); await root.InvokeAsync(args); diff --git a/test/TimeItSharp.FluentConfiguration.Sample/Program.cs b/test/TimeItSharp.FluentConfiguration.Sample/Program.cs index aa3b5ef..4326465 100644 --- a/test/TimeItSharp.FluentConfiguration.Sample/Program.cs +++ b/test/TimeItSharp.FluentConfiguration.Sample/Program.cs @@ -10,6 +10,7 @@ .WithCount(100) .WithName("dd-tracer-dotnet test") .WithMetrics(true) + .WithMetricsProcessName("dotnet") .WithExporters() .WithAssertor() .WithService()