# ZLogger writes UTF8 straight from the interpolated string

> ZLogger is a Microsoft.Extensions.Logging provider for .NET that formats log messages into UTF8 bytes without allocating a UTF16 string, using C# 10 string interpolation and IUtf8SpanFormattable on .NET 8. The win is on hot logging paths; the cost is a .NET-centric feature set and a version skew between the repository and NuGet.

**Cysharp/ZLogger** — Zero Allocation Text/Structured Logger for .NET with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging.

- Repository: https://github.com/Cysharp/ZLogger
- Stars: 1,782 · Forks: 131
- Language: C#
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/cysharp-zlogger

## The allocation that never happens

Log output usually ends in a UTF8 stream, whether that is a console, a file or a network socket. The pipeline in between is built on strings, which are UTF16, so a conventional logger allocates an intermediate UTF16 buffer and then encodes it. ZLogger's argument is that this conversion is pure overhead.

Its approach uses the string interpolation improvement in C# 10 and .NET 8's IUtf8SpanFormattable. A handler receives the interpolated string and writes UTF8 bytes directly, so values are formatted straight into the destination buffer. The README describes the result as avoiding the boxing of values and maintaining performance by outputting directly in UTF8 from input to output.

The second half of the design is structural. ZLogger is built directly on Microsoft.Extensions.Logging, the abstraction used by ASP.NET Core and the Generic Host. Other logging systems need a bridge to connect to that abstraction, and the README points at that bridge as a source of overhead, which ZLogger removes by being the provider itself rather than a layer beside it.

## Setup is two calls in an ASP.NET Core host

The library ships on NuGet and supports .NET Standard 2.0, .NET Standard 2.1, .NET 6 with .NET 7, and .NET 8 or above. Installation is a single package reference:

```csharp
> dotnet add package ZLogger
```

Wiring it into an ASP.NET Core application clears the default providers and adds the ZLogger console one:

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddZLoggerConsole();
```

From there, a logger injected through DI exposes the Z prefixed methods, and the interpolated string handler carries the parameters:

```csharp
@inject ILogger<Index> logger
@{
    logger.ZLogInformation($"Requested path: {this.HttpContext.Request.Path}");
}
```

The Z prefix is what keeps this from colliding with the Microsoft.Extensions.Logging extensions, and it is how you tell at a call site that the fast path is in play.

## Console, file, rolling file, in-memory, stream and an HTTP processor

The provider list covers the usual destinations: Console, File, RollingFile, InMemory and Stream, plus an AsyncBatchingProcessor described as being for sending logs over HTTP and similar protocols.

That last one is the piece worth pausing on in a cloud deployment. An asynchronous batching processor buffers records and sends them in batches rather than blocking the request thread on a socket write, which matters when the log destination is a network endpoint rather than local disk.

Standard Microsoft.Extensions.Logging features are carried over: loading LogLevel from JSON, filtering by category, and scopes. A library that reimplements the provider surface usually loses one of these, and this one does not, which is the main reason the abstraction holds up.

## Plain text, JSON with a key mutator, and MessagePack

Formatters are a separate axis from providers. Plain text is the default output. JSON uses System.Text.Json's Utf8JsonWriter, integrated closely enough that structured output does not undo the UTF8 advantage. MessagePack is offered as a further format.

The JSON formatter exposes a KeyNameMutator, which renames keys on the way out. In practice that is how you make structured logs match a collector's expected schema, for example flattening or shortening a property name, without changing the type that produced it.

There is also a custom formatter hook for output shapes that are neither text, JSON nor MessagePack. The README's table of contents lists Formatter Configurations with PlainText, JSON, KeyNameMutator, MessagePack and Custom Formatter as the options, which is the surface to read before deciding ZLogger cannot meet your sink's requirements.

## ZLoggerMessage and the banned API analyzer

Beyond the interpolated handler path, the project ships a source generator named ZLoggerMessage, listed alongside LogInfo and ZLoggerOptions in the README's contents. It is the compile time route to the same output, generating message handling so the work moves to build time rather than run time.

The repository also references Microsoft.CodeAnalysis.BannedApiAnalyzers in its own table of contents, which is a Roslyn analyzer rather than a feature of the logger. Its presence suggests the project uses it to keep accidental use of slow APIs inside its own code and possibly to guide downstream usage.

Both of these belong in an evaluation checklist rather than an adoption decision on their own. A generated path and a handler path behave the same at the call site, and which one to use depends on how much of your logging is compile time stable.

## Where the speed claims stop applying

The README is unusually candid about the limits of its own benchmark, and that candour is the most useful part of it. The published comparison is for writing to a file, and it notes that typical loggers look slow largely because they flush after every write, and that the benchmark was tuned to be fair by setting each logger's options for maximum speed. ZLogger's claim is that it is fastest by default so there is no configuration to get wrong.

It also states the caveat plainly: the slowness comes from I/O, results vary greatly between a fast local drive and drives attached to cloud or Docker environments, and the async buffered setting exists to reduce that impact. In other words, on a slow disk the ranking can change.

Add to that the platform boundary. Best performance arrives on .NET 8 and above; on .NET Standard 2.0 and .NET 6 the library falls back to its own IUtf8SpanFormattable, which is a different code path. If your runtime is not .NET 8, the headline numbers do not describe your build.

## Licence, packaging and the release gap

ZLogger is MIT licensed, with the LICENSE file at the repository root and the same Cysharp organisation behind ConsoleAppFramework, which the README offers as the hosting abstraction for console applications.

The repository layout shows a solution file ZLogger.sln, src/ and tests/ directories, a sandbox/ for experiments, a docs/ folder holding the benchmark images, and opensource.snk for strong naming. Directory.Build.props at the root suggests shared build settings across projects.

The version skew is the thing to plan around. The most recent releases listed are 2.5.10 on 2025-01-06, 2.5.9 on 2024-12-05 and 2.5.8 on 2024-11-20, while the last push was on 2026-10-07. Commits are landing after the last published package, so pin an explicit version rather than floating, and check whether the version you pin includes the changes you actually need.

## Conclusion

Adopt ZLogger in a .NET 8 or later service where logging sits on a hot path and the destination is console, file or a JSON collector, and where your team is comfortable with interpolated string handlers. Do not adopt it for a .NET Framework project or for Unity without reading the separate Unity section, and do not expect it to beat a buffered writer on slow disks. Verify first the NuGet package version you are pinning, since the last release listed here is 2.5.10 from 2025-01-06 while the repository's last push was on 2026-10-07.

## FAQ

### How do I install ZLogger in a .NET project?

Add the NuGet package with dotnet add package ZLogger. The library supports .NET Standard 2.0, .NET Standard 2.1, .NET 6 with .NET 7, and .NET 8 or above, and Unity follows a separate installation path documented in its own README section.

### How do I enable ZLogger in ASP.NET Core?

Clear the default providers and add the ZLogger console provider on the host builder: builder.Logging.ClearProviders() followed by builder.Logging.AddZLoggerConsole(). Loggers obtained from dependency injection then expose the ZLog prefixed methods.

### Does ZLogger support structured logging?

Yes. It has JSON and MessagePack formatters alongside plain text, with JSON built on System.Text.Json's Utf8JsonWriter and a KeyNameMutator for renaming keys before output. A custom formatter hook is listed as well.

### Which output destinations does ZLogger support?

Console, File, RollingFile, InMemory and Stream, plus an AsyncBatchingProcessor for sending logs over HTTP and similar protocols. LogLevel can be loaded from JSON, and filtering by category and scopes work as they do in Microsoft.Extensions.Logging.

## Sources

- [Cysharp/ZLogger on GitHub](https://github.com/Cysharp/ZLogger)
- [Issues](https://github.com/Cysharp/ZLogger/issues)
- [License: MIT](https://github.com/Cysharp/ZLogger/blob/master/LICENSE)
- [README](https://github.com/Cysharp/ZLogger/blob/master/README.md)
- [Releases](https://github.com/Cysharp/ZLogger/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cysharp-zlogger
