Skip to content

Output

Reporters consume the finished BenchmarkResult list and produce output - terminal tables, Markdown files, CSVs, or JSON. You can attach as many reporters as you like to a single run.

In this section

How reporters work

All reporters implement IReporter:

csharp
public interface IReporter
{
    string Name { get; }

    Task ReportAsync(IReadOnlyList<BenchmarkResult> results, CancellationToken cancellationToken = default);
}

The Name property identifies the reporter for the --reporter CLI flag and the --output directory rewriting. Built-in reporters return their canonical name ("json", "markdown", "csv", "console"). Custom reporters may return any unique string.

Reporters are called after all benchmarks in the run have completed. They receive the full result list including any errored benchmarks.

Attaching reporters

BenchmarkSuite (Suite mode)

csharp
await new BenchmarkSuite("name")
    .WithReporter(new ConsoleReporter())
    .WithReporter(new MarkdownReporter("results/"))
    .WithReporter(new CsvReporter("results/"))
    .RunAsync();

BenchmarkHarness (Harness mode)

csharp
BenchmarkHarness.Create(args)
    .WithReporter(new ConsoleReporter())
    .WithReporter(new JsonReporter("results/"))
    .RunAsync();

Benchmark (Single mode) - extension methods

csharp
var result = Benchmark.Run(() => MyMethod());

await result.ToMarkdownAsync("results/");
await result.ToCsvAsync("results/");
await result.ToJsonAsync("results/");

Available reporters

ReporterPackageOutput
ConsoleReporterNBenchmark.Reporters.ConsoleRich terminal table with colour and a bar chart
MarkdownReporterNBenchmark.md file with a formatted results table
CsvReporterNBenchmark.csv file with all statistics, suitable for post-processing
JsonReporterNBenchmark.json file with full structured results

Output path validation

File reporters validate that the output directory is under the current working directory. Paths outside the CWD (e.g. /tmp/results or ../../other-project) are rejected with an ArgumentException. This prevents accidental writes outside the project directory.

csharp
// Works - relative path under CWD
new MarkdownReporter("results/")

// Throws ArgumentException - outside CWD
new MarkdownReporter("/tmp/results/")

The output directory is created automatically if it does not exist.

Using the CLI reporter flag

With BenchmarkHarness, the --reporter CLI flag adds reporters by name:

bash
dotnet run -- --reporter markdown --output ./results
dotnet run -- --reporter csv
dotnet run -- --reporter json
dotnet run -- --reporter console   # works when NBenchmark.Reporters.Console is referenced

The --reporter flag constructs reporters through ReporterRegistry.TryCreate, which handles both built-in reporters (json/markdown/csv) and any reporters self-registered by external packages.

External packages (like NBenchmark.Reporters.Console) self-register via [ModuleInitializer] + ReporterRegistry.Register(). The --reporter flag discovers available reporters automatically - no per-reporter code changes needed in BenchmarkHarness.

If you reference an unknown reporter name, the host prints the list of available reporters plus a hint about the console package.

Detail levels

Reporters support three detail levels - Simple (default), Standard, and Advanced - that control how much statistical information is included in the output. Set the level via WithDetail(ReportDetail.Standard) on both BenchmarkHarness and BenchmarkSuite, or via the --detail standard CLI flag in harness mode. See the Report Detail Levels guide for the full column reference.

Writing a custom reporter

See the Custom Reporters page for a step-by-step guide to implementing IReporter, registering it with ReporterRegistry, and using BenchmarkTable for comparison output. That page also documents auto-attached reporters (ReporterRegistry.RegisterAutoAttach) - side-effect reporters that fire on every run after the user's explicit reporters, with no opt-in required.

Released under the MIT License.