Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
cedc672
fix: migrate PerfDiff to System.CommandLine 2.0.3
rjmurillo Mar 7, 2026
bbb8e62
fix(PerfDiff): restore console color on exception and avoid repeated …
rjmurillo Mar 7, 2026
eaac9bc
fix(PerfDiff): dispose ServiceProvider after use
rjmurillo Mar 7, 2026
ebed699
refactor(PerfDiff): simplify ServiceProvider disposal and remove dead…
rjmurillo Mar 8, 2026
4ab30c3
fix(deps): update Perfolizer version to 0.6.7
rjmurillo Mar 8, 2026
1d5a79b
fix(perfdiff): make --baseline and --results options required
rjmurillo Mar 8, 2026
26c2062
fix(perfdiff): wire failOnRegression flag through CompareAsync properly
rjmurillo Mar 8, 2026
4893082
fix(PerfDiff): return exit code from SetAction lambda
rjmurillo Mar 8, 2026
1434e6b
refactor: simplify code in PR #1043
rjmurillo Mar 8, 2026
53ead04
refactor(PerfDiff): fix System.CommandLine 2.0.3 API and reduce noise
rjmurillo Mar 8, 2026
1dee067
fix: register LoggerFactory via factory delegate for proper disposal
rjmurillo Mar 8, 2026
6328dfb
Merge remote-tracking branch 'origin/main' into fix/914-perfdiff-syst…
rjmurillo Mar 8, 2026
b31b679
fix: resolve LoggerFactory double registration and narrow accessibility
rjmurillo Mar 8, 2026
7b8216b
fix: narrow Program.RunAsync accessibility to internal
rjmurillo Mar 8, 2026
5a78546
Merge remote-tracking branch 'origin/main' into fix/914-perfdiff-syst…
rjmurillo Mar 8, 2026
29ec00d
fix: update package snapshot files to include THIRD-PARTY-NOTICES.TXT
rjmurillo Mar 8, 2026
9cd3587
refactor: eliminate DRY violation and use FrozenDictionary in PerfDiff
rjmurillo Mar 8, 2026
a4c4dd1
Merge remote-tracking branch 'origin/main' into fix/914-perfdiff-syst…
rjmurillo Mar 9, 2026
bec8f65
refactor: consolidate verbosity mapping into single source of truth
rjmurillo Mar 9, 2026
e63d03c
Merge branch 'main' into fix/914-perfdiff-system-commandline
rjmurillo Mar 9, 2026
3c27914
fix: make logger gate lock static to prevent concurrent color interle…
rjmurillo Mar 9, 2026
8fbd3e3
Merge branch 'main' into fix/914-perfdiff-system-commandline
rjmurillo Mar 9, 2026
c1d3c60
fix(deps): pin Perfolizer to 0.6.1 to match BenchmarkDotNet 0.15.8
rjmurillo Mar 9, 2026
85c6680
test: update generic callback test to expect diagnostic
rjmurillo Mar 9, 2026
4a550bb
fix: replace const with static readonly to satisfy ECS0200
rjmurillo Mar 9, 2026
59d887a
style: mark SetAction lambda as static to prevent accidental closures
rjmurillo Mar 9, 2026
59f3af8
refactor: remove redundant StringComparer from Dictionary constructor
rjmurillo Mar 9, 2026
9027ccd
refactor: set explicit Dictionary capacity and cache logger instance
rjmurillo Mar 9, 2026
10c6afb
fix: promote regression log messages to Warning and set factory minim…
rjmurillo Mar 9, 2026
c4a2bfb
Merge branch 'main' into fix/914-perfdiff-system-commandline
rjmurillo Mar 9, 2026
357a530
Merge branch 'fix/914-perfdiff-system-commandline' of https://github.…
rjmurillo Mar 9, 2026
4e1b985
merge: update with latest main (meziantou.analyzer 3.0.21)
rjmurillo Mar 9, 2026
9fedf86
revert: restore const for PerfDiff fields (ECS0200 is disabled)
rjmurillo Mar 9, 2026
f5efa1f
fix: log full exception for FileNotFoundException in PerfDiff
rjmurillo Mar 9, 2026
d5adf9d
chore(deps): update dependency meziantou.analyzer to 3.0.22 (#1059)
renovate[bot] Mar 10, 2026
de0e280
Merge remote-tracking branch 'origin/main' into fix/914-perfdiff-syst…
rjmurillo Mar 10, 2026
53bc572
fix(PerfDiff): thread CancellationToken through async call chain
rjmurillo Mar 14, 2026
69978a3
fix(PerfDiff): replace SingleOrDefault with FirstOrDefault for ETL files
rjmurillo Mar 14, 2026
374d9cf
docs(PerfDiff): add XML param tags for CancellationToken parameters
rjmurillo Mar 14, 2026
74429a3
refactor(PerfDiff): simplify LINQ patterns and fix structured logging
rjmurillo Mar 14, 2026
2daa71d
test(PerfDiff): add tests for CancellationToken, ETL paths, and CLI c…
rjmurillo Mar 14, 2026
f1765f5
fix(PerfDiff): resolve unused variable warning in RunAsync
rjmurillo Mar 14, 2026
44efced
merge: update with latest main
rjmurillo Mar 14, 2026
4cfa058
Merge branch 'main' into fix/914-perfdiff-system-commandline
rjmurillo Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="GetPackFromProject" Version="1.0.10" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.9.50" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta1.21216.1" />
<PackageVersion Include="System.CommandLine.Rendering" Version="2.0.0-beta1.20074.1" />
<PackageVersion Include="System.CommandLine" Version="2.0.3" />
Comment thread
rjmurillo marked this conversation as resolved.
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Perfolizer" Version="0.6.1" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.5" />
Expand Down
5 changes: 3 additions & 2 deletions src/tools/PerfDiff/BDN/BenchmarkComparisonService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ public class BenchmarkComparisonService(ILogger logger)
/// </summary>
/// <param name="baselineFolder">The folder containing baseline results.</param>
/// <param name="resultsFolder">The folder containing new results.</param>
/// <param name="cancellationToken">Token to observe for cancellation requests.</param>
/// <returns>A <see cref="BenchmarkComparisonResult"/> indicating comparison success and regression detection.</returns>
public async Task<BenchmarkComparisonResult> CompareAsync(string baselineFolder, string resultsFolder)
public async Task<BenchmarkComparisonResult> CompareAsync(string baselineFolder, string resultsFolder, CancellationToken cancellationToken)
{
BdnComparisonResult[]? comparison = await BenchmarkDotNetDiffer.TryGetBdnResultsAsync(baselineFolder, resultsFolder, logger).ConfigureAwait(false);
BdnComparisonResult[]? comparison = await BenchmarkDotNetDiffer.TryGetBdnResultsAsync(baselineFolder, resultsFolder, logger, cancellationToken).ConfigureAwait(false);
if (comparison is null)
{
return new BenchmarkComparisonResult(CompareSucceeded: false, RegressionDetected: false);
Expand Down
30 changes: 19 additions & 11 deletions src/tools/PerfDiff/BDN/BenchmarkDotNetDiffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
/// <param name="baselineFolder">The folder containing baseline results.</param>
/// <param name="resultsFolder">The folder containing new results.</param>
/// <param name="logger">Logger for reporting errors.</param>
/// <param name="cancellationToken">Token to observe for cancellation requests.</param>
/// <returns>A <see cref="BenchmarkComparisonResult"/> indicating comparison success and regression detection.</returns>
public static async Task<BenchmarkComparisonResult> TryCompareBenchmarkDotNetResultsAsync(string baselineFolder, string resultsFolder, ILogger logger)
public static async Task<BenchmarkComparisonResult> TryCompareBenchmarkDotNetResultsAsync(string baselineFolder, string resultsFolder, ILogger logger, CancellationToken cancellationToken)
{
BenchmarkComparisonService service = new(logger);
return await service.CompareAsync(baselineFolder, resultsFolder).ConfigureAwait(false);
return await service.CompareAsync(baselineFolder, resultsFolder, cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -37,8 +38,9 @@
/// <param name="baselineFolder">The folder containing baseline results.</param>
/// <param name="resultsFolder">The folder containing new results.</param>
/// <param name="logger">Logger for reporting errors.</param>
/// <param name="cancellationToken">Token to observe for cancellation requests.</param>
/// <returns>An array of <see cref="BdnComparisonResult"/> if successful; otherwise, <see langword="null"/>.</returns>
internal static async Task<BdnComparisonResult[]?> TryGetBdnResultsAsync(string baselineFolder, string resultsFolder, ILogger logger)
internal static async Task<BdnComparisonResult[]?> TryGetBdnResultsAsync(string baselineFolder, string resultsFolder, ILogger logger, CancellationToken cancellationToken)

Check warning on line 43 in src/tools/PerfDiff/BDN/BenchmarkDotNetDiffer.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/tools/PerfDiff/BDN/BenchmarkDotNetDiffer.cs#L43

Method BenchmarkDotNetDiffer::TryGetBdnResultsAsync has a cyclomatic complexity of 19 (limit is 8)
{
if (!TryGetFilesToParse(baselineFolder, out string[]? baseFiles))
{
Expand All @@ -52,19 +54,19 @@
return null;
}

if (!baseFiles.Any() || !resultsFiles.Any())
if (baseFiles.Length == 0 || resultsFiles.Length == 0)
{
logger.LogError($"Provided paths contained no '{FullBdnJsonFileExtension}' files.");
logger.LogError("Provided paths contained no '{FileExtension}' files.", FullBdnJsonFileExtension);
return null;
}

(bool baseResultsSuccess, BdnResult?[] baseResults) = await BenchmarkFileReader.TryGetBdnResultAsync(baseFiles, logger).ConfigureAwait(false);
(bool baseResultsSuccess, BdnResult?[] baseResults) = await BenchmarkFileReader.TryGetBdnResultAsync(baseFiles, logger, cancellationToken).ConfigureAwait(false);
if (!baseResultsSuccess)
{
return null;
}

(bool resultsSuccess, BdnResult?[] diffResults) = await BenchmarkFileReader.TryGetBdnResultAsync(resultsFiles, logger).ConfigureAwait(false);
(bool resultsSuccess, BdnResult?[] diffResults) = await BenchmarkFileReader.TryGetBdnResultAsync(resultsFiles, logger, cancellationToken).ConfigureAwait(false);
if (!resultsSuccess)
{
return null;
Expand All @@ -78,10 +80,16 @@
.SelectMany(result => result?.Benchmarks ?? Enumerable.Empty<Benchmark>())
.ToDictionary(benchmarkResult => benchmarkResult.FullName ?? $"Unknown-{Guid.NewGuid():N}", benchmarkResult => benchmarkResult);

return benchmarkIdToBaseResults
.Where(baseResult => benchmarkIdToDiffResults.ContainsKey(baseResult.Key))
.Select(baseResult => new BdnComparisonResult(baseResult.Key, baseResult.Value, benchmarkIdToDiffResults[baseResult.Key]))
.ToArray();
List<BdnComparisonResult> matched = [];
foreach (KeyValuePair<string, Benchmark> baseResult in benchmarkIdToBaseResults)
{
if (benchmarkIdToDiffResults.TryGetValue(baseResult.Key, out Benchmark? diffBenchmark))
{
matched.Add(new BdnComparisonResult(baseResult.Key, baseResult.Value, diffBenchmark));
}
}

return matched.ToArray();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with many returns (count = 6): TryGetBdnResultsAsync [qlty:return-statements]

}

private static bool TryGetFilesToParse(string path, [NotNullWhen(true)] out string[]? files)
Expand Down
11 changes: 6 additions & 5 deletions src/tools/PerfDiff/BDN/BenchmarkFileReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@ internal static class BenchmarkFileReader
/// </summary>
/// <param name="paths">Array of file paths to read.</param>
/// <param name="logger">Logger for reporting errors.</param>
/// <param name="cancellationToken">Token to observe for cancellation requests.</param>
/// <returns>A <see cref="BdnResults"/> containing the loaded results and success status.</returns>
public static async Task<BdnResults> TryGetBdnResultAsync(string[] paths, ILogger logger)
public static async Task<BdnResults> TryGetBdnResultAsync(string[] paths, ILogger logger, CancellationToken cancellationToken)
{
BdnResult?[] results = await Task.WhenAll(paths.Select(path => ReadFromFileAsync(path, logger))).ConfigureAwait(false);
return new BdnResults(!results.Any(static x => x is null), results);
BdnResult?[] results = await Task.WhenAll(paths.Select(path => ReadFromFileAsync(path, logger, cancellationToken))).ConfigureAwait(false);
return new BdnResults(Array.TrueForAll(results, static x => x is not null), results);
}

private static async Task<BdnResult?> ReadFromFileAsync(string resultFilePath, ILogger logger)
private static async Task<BdnResult?> ReadFromFileAsync(string resultFilePath, ILogger logger, CancellationToken cancellationToken)
{
try
{
return JsonConvert.DeserializeObject<BdnResult>(await File.ReadAllTextAsync(resultFilePath).ConfigureAwait(false));
return JsonConvert.DeserializeObject<BdnResult>(await File.ReadAllTextAsync(resultFilePath, cancellationToken).ConfigureAwait(false));
}
catch (Exception ex) when (ex is JsonReaderException or JsonSerializationException or IOException or UnauthorizedAccessException or SecurityException)
{
Expand Down
89 changes: 66 additions & 23 deletions src/tools/PerfDiff/DiffCommand.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Frozen;
using System.CommandLine;
using Microsoft.Extensions.Logging;

namespace PerfDiff;

Expand All @@ -10,43 +12,84 @@ namespace PerfDiff;
internal static class DiffCommand
{
/// <summary>
/// Delegate for handling the diff command.
/// Maps verbosity strings to their corresponding log levels.
/// </summary>
/// <param name="baseline">Baseline results folder.</param>
/// <param name="results">Results folder.</param>
/// <param name="verbosity">Verbosity level.</param>
/// <param name="failOnRegression">Whether to fail on regression.</param>
/// <param name="console">Console for output.</param>
/// <returns>Exit code.</returns>
internal delegate Task<int> Handler(
string baseline,
string results,
string? verbosity,
bool failOnRegression,
IConsole console);
private static readonly FrozenDictionary<string, LogLevel> VerbosityMap = new Dictionary<string, LogLevel>(10)
{
["q"] = LogLevel.Error,
["quiet"] = LogLevel.Error,
["m"] = LogLevel.Warning,
["minimal"] = LogLevel.Warning,
["n"] = LogLevel.Information,
["normal"] = LogLevel.Information,
["d"] = LogLevel.Debug,
["detailed"] = LogLevel.Debug,
["diag"] = LogLevel.Trace,
["diagnostic"] = LogLevel.Trace,
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);

/// <summary>
/// Gets the baseline option.
/// </summary>
internal static Option<string> BaselineOption { get; } = CreateFilePathOption("--baseline", "folder that contains the baseline performance run data");

/// <summary>
/// Gets the allowed verbosity levels for the command.
/// Gets the results option.
/// </summary>
private static readonly string[] VerbosityLevels = ["q", "quiet", "m", "minimal", "n", "normal", "d", "detailed", "diag", "diagnostic"];
internal static Option<string> ResultsOption { get; } = CreateFilePathOption("--results", "folder that contains the performance results");

/// <summary>
/// Gets the verbosity option.
/// </summary>
internal static Option<string> VerbosityOption { get; } = CreateVerbosityOption();

/// <summary>
/// Gets the fail-on-regression option.
/// </summary>
internal static Option<bool> FailOnRegressionOption { get; } = new("--failOnRegression")
{
Description = "Should return non-zero exit code if regression detected",
};

private static Option<string> CreateFilePathOption(string name, string description)
{
Option<string> option = new(name) { Description = description, Required = true };
option.AcceptLegalFilePathsOnly();
return option;
}

/// <summary>
/// Returns the <see cref="LogLevel"/> for the given verbosity string.
/// Falls back to <see cref="LogLevel.Information"/> when the value is null or unrecognized.
/// </summary>
/// <param name="verbosity">The verbosity string from the command line.</param>
/// <returns>The corresponding <see cref="LogLevel"/>.</returns>
internal static LogLevel GetLogLevel(string? verbosity)
=> verbosity is not null && VerbosityMap.TryGetValue(verbosity, out LogLevel level)
? level
: LogLevel.Information;

private static Option<string> CreateVerbosityOption()
{
Option<string> option = new("--verbosity", "-v") { Description = "Set the verbosity level. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]" };
option.AcceptOnlyFromAmong(VerbosityMap.Keys.ToArray());
return option;
}

/// <summary>
/// Creates the root command with options for the diff command.
/// </summary>
/// <returns>The configured <see cref="RootCommand"/>.</returns>
internal static RootCommand CreateCommandLineOptions()
{
// Sync changes to option and argument names with the FormatCommand.Handler above.
RootCommand rootCommand = new RootCommand
RootCommand rootCommand = new RootCommand("diff two sets of performance results")
{
new Option<string?>("--baseline", () => null, "folder that contains the baseline performance run data").LegalFilePathsOnly(),
new Option<string?>("--results", () => null, "folder that contains the performance results").LegalFilePathsOnly(),
new Option<string>(["--verbosity", "-v"], "Set the verbosity level. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]").FromAmong(VerbosityLevels),
new Option<bool>(["--failOnRegression"], "Should return non-zero exit code if regression detected"),
BaselineOption,
ResultsOption,
VerbosityOption,
FailOnRegressionOption,
};

rootCommand.Description = "diff two sets of performance results";

return rootCommand;
}
}
46 changes: 16 additions & 30 deletions src/tools/PerfDiff/Logging/SimpleConsoleLogger.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.CommandLine;
using System.CommandLine.Rendering;
using System.Collections.Frozen;
using Microsoft.Extensions.Logging;

namespace PerfDiff.Logging;
Expand All @@ -12,14 +10,12 @@ namespace PerfDiff.Logging;
/// </summary>
internal sealed class SimpleConsoleLogger : ILogger
{
private readonly Lock _gate = new();
private static readonly Lock Gate = new();

private readonly IConsole _console;
private readonly ITerminal _terminal;
private readonly LogLevel _minimalLogLevel;
private readonly LogLevel _minimalErrorLevel;

private static ImmutableDictionary<LogLevel, ConsoleColor> LogLevelColorMap => new Dictionary<LogLevel, ConsoleColor>
private static readonly FrozenDictionary<LogLevel, ConsoleColor> LogLevelColorMap = new Dictionary<LogLevel, ConsoleColor>
{
[LogLevel.Critical] = ConsoleColor.Red,
[LogLevel.Error] = ConsoleColor.Red,
Expand All @@ -28,18 +24,15 @@ internal sealed class SimpleConsoleLogger : ILogger
[LogLevel.Debug] = ConsoleColor.Gray,
[LogLevel.Trace] = ConsoleColor.Gray,
[LogLevel.None] = ConsoleColor.White,
}.ToImmutableDictionary();
}.ToFrozenDictionary();

/// <summary>
/// Initializes a new instance of the <see cref="SimpleConsoleLogger"/> class.
/// </summary>
/// <param name="console">The console to write output to.</param>
/// <param name="minimalLogLevel">The minimal log level for output.</param>
/// <param name="minimalErrorLevel">The minimal log level for error output.</param>
public SimpleConsoleLogger(IConsole console, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
public SimpleConsoleLogger(LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
{
_terminal = console.GetTerminal();
_console = console;
_minimalLogLevel = minimalLogLevel;
_minimalErrorLevel = minimalErrorLevel;
}
Expand All @@ -52,17 +45,20 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
return;
}

lock (_gate)
lock (Gate)
{
string message = formatter(state, exception);
bool logToErrorStream = logLevel >= _minimalErrorLevel;
if (_terminal is null)
ConsoleColor messageColor = LogLevelColorMap[logLevel];

Console.ForegroundColor = messageColor;
try
{
LogToConsole(_console, message, logToErrorStream);
LogToConsole(message, logToErrorStream);
}
else
finally
{
LogToTerminal(message, logLevel, logToErrorStream);
Console.ResetColor();
}
}
}
Expand All @@ -80,25 +76,15 @@ public IDisposable BeginScope<TState>(TState state)
return NullScope.Instance;
}

private void LogToTerminal(string message, LogLevel logLevel, bool logToErrorStream)
{
ConsoleColor messageColor = LogLevelColorMap[logLevel];
_terminal.ForegroundColor = messageColor;

LogToConsole(_terminal, message, logToErrorStream);

_terminal.ResetColor();
}

private static void LogToConsole(IConsole console, string message, bool logToErrorStream)
private static void LogToConsole(string message, bool logToErrorStream)
{
if (logToErrorStream)
{
console.Error.Write($"{message}{Environment.NewLine}");
Console.Error.Write($"{message}{Environment.NewLine}");
}
else
{
console.Out.Write($" {message}{Environment.NewLine}");
Console.Out.Write($" {message}{Environment.NewLine}");
}
}
}

This file was deleted.

14 changes: 4 additions & 10 deletions src/tools/PerfDiff/Logging/SimpleConsoleLoggerProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.CommandLine;
using Microsoft.Extensions.Logging;

namespace PerfDiff.Logging;
Expand All @@ -10,27 +9,22 @@ namespace PerfDiff.Logging;
/// </summary>
internal sealed class SimpleConsoleLoggerProvider : ILoggerProvider
{
private readonly IConsole _console;
private readonly LogLevel _minimalLogLevel;
private readonly LogLevel _minimalErrorLevel;
private readonly SimpleConsoleLogger _logger;

/// <summary>
/// Initializes a new instance of the <see cref="SimpleConsoleLoggerProvider"/> class.
/// </summary>
/// <param name="console">The console to write output to.</param>
/// <param name="minimalLogLevel">The minimal log level for output.</param>
/// <param name="minimalErrorLevel">The minimal log level for error output.</param>
public SimpleConsoleLoggerProvider(IConsole console, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
public SimpleConsoleLoggerProvider(LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
{
_console = console;
_minimalLogLevel = minimalLogLevel;
_minimalErrorLevel = minimalErrorLevel;
_logger = new SimpleConsoleLogger(minimalLogLevel, minimalErrorLevel);
}

/// <inheritdoc/>
public ILogger CreateLogger(string categoryName)
{
return new SimpleConsoleLogger(_console, _minimalLogLevel, _minimalErrorLevel);
return _logger;
}

/// <inheritdoc/>
Expand Down
Loading
Loading