Skip to content

Benchmarks

Mykhailo Shevchuk edited this page Jun 6, 2026 · 3 revisions

These numbers come from the BenchmarkDotNet suites in the benchmarks/ folder - see its README to reproduce them. Each sink is driven end to end through its public API, pushing batches of log events at an in-process transport that returns Loki's 204. So what is measured is the sink's own work - serialization plus batching - with the network taken out of the picture.

Three sinks are benchmarked:

  • v8 - Serilog.Sinks.Grafana.Loki 8.3.2, the previous release (Serilog 2.12)
  • v9 - this sink, the Serilog 4.x rewrite
  • YetAnother - Serilog.Sinks.Loki.YetAnother 4.0.5, a popular, allocation-conscious third-party Loki sink (Serilog 4.x)

This page focuses on the v8 → v9 upgrade. YetAnother is included as an external yardstick - a well-regarded low-allocation sink, run through the identical harness - so v9 is measured against the best of the field, not just against its own past. Its per-sink figures sit alongside v8/v9 in that folder's README.

Each event is either Simple (a templated message with int / string / float / bool / Guid properties) or Exception (a two-level nested exception with real stack traces). Lower is better throughout.

v8 → v9

v9 changed the serialization design. v8 built an intermediate object graph and rendered it to a JSON string for every batch; v9 streams JSON bytes straight through a reused, pooled Utf8JsonWriter buffer, with no intermediate string - then a focused optimization pass took it much further (see V9 Optimization Log).

Workload Allocated (v8 → v9) Time (v8 → v9)
Simple · 1 000 7.5 MB → 0.14 MB 11.5 ms → 5.4 ms
Simple · 10 000 75 MB → 1.36 MB (~55× less) 79 ms → 18 ms (~4.5× faster)
Exception · 1 000 21.9 MB → 6.6 MB 25.1 ms → 33.5 ms (noisy)
Exception · 10 000 220 MB → 66 MB (−70 %) 263 ms → 121 ms (~2.2× faster)

On the common (Simple) workload v9 allocates ~55× less than v8 and runs ~4.5× faster. v8 also pushed its large intermediate strings onto the Gen2/LOH heap; v9 stays in Gen0 on that path.

Exception logging improves less, because materialising .NET stack traces (ex.StackTrace) is a per-event cost unrelated to the sink, and it dominates that workload for any sink.

How to read this

  • The 10 000-event rows are the throughput numbers. At 1 000 events a fixed per-flush overhead dominates and the means get noisy, so small-batch timings are not a good guide to per-event cost (the Exception · 1 000 time above is one such noisy case).
  • Allocation figures are exact (BenchmarkDotNet's memory diagnoser); timings are wall-clock means on an AMD Ryzen 9 3950X, .NET 8.
  • v8 is a fixed NuGet package (a stable reference point); v9 is the current build.
  • The benchmark uses an in-process transport; against a real Loki the network and ingestion add latency on top, equally for every sink.

Running them

The suites run through the repository's FAKE build script, so the same command works on Windows, Linux and macOS:

# from the repo root - all three suites, fake transport (default)
dotnet fsi build.fsx -- --target Benchmark

Optional environment variables: BENCH_FILTER (a BenchmarkDotNet --filter glob such as *Sink*) and LOKI_BENCH_TARGET (push to a real Loki started via docker compose up -d loki instead of the in-process fake).

See also

Clone this wiki locally