Zero Allocation Text/Structured Logger for .NET with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging.
Zero Allocation Text/Structured Logger for .NET with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging.
Zero Allocation Text/Structured Logger for .NET and Unity, with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging.
The usual destinations for log output are Console(Stream), File(Stream), Network(Stream), all in UTF8 format. However, since typical logging architectures are based on Strings (UTF16), this requires additional encoding costs. In ZLogger, we utilize the String Interpolation Improvement of C# 10 and by leveraging .NET 8's IUtf8SpanFormattable, we have managed to avoid the boxing of values and maintain high performance by consistently outputting directly in UTF8 from input to output.
ZLogger is built directly on top of Microsoft.Extensions.Logging. Microsoft.Extensions.Logging is an official log abstraction used in many frameworks, such as ASP.NET Core and Generic Host. However, since regular loggers have their own systems, a bridge is required to connect these systems, and this is where a lot of overhead can be observed. ZLogger eliminates the need for this bridge, thereby completely avoiding overhead.
This benchmark is for writing to a file, but the default settings of typical loggers are very slow. This is because they flush after every write. In the benchmark, to ensure fairness, careful attention was paid to set the options in each logger for maximum speed. ZLogger is designed to be the fastest by default, so there is no need to worry about any settings.
The slowness of this default setting is due to I/O, so it can be mitigated by using a faster drive. When taking benchmarks, please note that the results can vary greatly not only on your local (which is probably fast!) but also on drives attached to the cloud and in environments like Docker. One of the good points about the async-buffered setting is that it can reduce the impact of such I/O issues.
ZLogger focuses on the new syntax of C#, and fully adopts Interpolated Strings.
This allows for providing parameters to logs in the most convenient form. Also, by closely integrating with System.Text.Json's Utf8JsonWriter, it not only enables high-performance output of text logs but also makes it possible to efficiently output structured logs.
ZLogger also emphasizes console output, which is crucial in cloud-native applications. By default, it outputs with performance that can withstand destinations in cloud log management. Of course, it supports both text logs and structured logs.
ZLogger delivers its best performance with .NET 8 and above, but it is designed to maintain consistent performance with .NET Standard 2.0 and .NET 6 through a fallback to its own IUtf8SpanFormattable.
As for standard logger features, it supports loading LogLevel from json, filtering by category, and scopes, as found in Microsoft.Extensions.Logging. In terms of output destinations, it is equipped with sufficient capabilities for Console, File, RollingFile, InMemory, Stream, and an AsyncBatchingProcessor for sending logs over HTTP and similar protocols.
This library is distributed via NuGet, supporting .NET Standard 2.0, .NET Standard 2.1, .NET 6(.NET 7) and .NET 8 or above.
For Unity, the requirements and installation process are completely different. See the Unity section for details.
dotnet add package ZLogger
Here is the most simple sample on ASP.NET Core.
using ZLogger;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddZLoggerConsole();
You can get logger from dependency injection.
@page
@using ZLogger;
@inject ILogger<Index> logger
@{
logger.ZLogInformation($"Requested path: {this.HttpContext.Request.Path}");
}
This simple logger setup is possible because it is integrated with Microsoft.Extensions.Logging by default. For reference, here's how you would set it up using LoggerFactory:
using Microsoft.Extensions.Logging;
using ZLogger;
using var factory = LoggerFactory.Create(logging =>
{
logging.SetMinimumLevel(LogLevel.Trace);
// Add ZLogger provider to ILoggingBuilder
logging.AddZLoggerConsole();
// Output Structured Logging, setup options
// logging.AddZLoggerConsole(options => options.UseJsonFormatter());
});
var logger = factory.CreateLogger("Program");
var name = "John";
var age = 33;
// Use **Z**Log method and string interpolation to log message
logger.ZLogInformation($"Hello my name is {name}, {age} years old.");
Normally, you don't create LoggerFactory yourself. Instead, you set up a Generic Host and receive ILogger through dependency injection (DI). You can setup logger by .NET Generic Host(for ASP.NET Core) and if you want to use this in ConsoleApplication, we provides ConsoleAppFramework to use hosting abstraction.
Here is the showcase of providers.
…
Look at the use of loggers and the syntax of ZLog.
…
All standard .Log methods are processed as strings by ZLogger's Provider. However, by using our unique .ZLog* methods, you can process them at high performance while remaining in UTF8. Additionally, these methods support both text logs and structured logs using String Interpolation syntax.
All logging methods are completely similar as Microsoft.Extensions.Logging.LoggerExtensions, but it has Z prefix overload.
The ZLog* method uses InterpolatedStringHandler in .NET and prepare the template at compile time.
Some special custom formats are also supported. The :@ can be used when you want to explicitly give the structured log a name other than the name of the variable to capture. :json can be used to log the result of JsonSerializing an object.
The @ parameter name specification and format string can be used together.
// Today is 2023-12-19.
// {"date":"2023-12-19T11:25:34.3642389+09:00"}
logger.ZLogDebug($"Today is {DateTime.Now:@date:yyyy-MM-dd}.");
By adding Providers, you can configure where the logs are output. ZLogger has the following providers.
| Type | Alias | Builder Extension |
|---|---|---|
| ZLoggerConsoleLoggerProvider | ZLoggerConsole | AddZLoggerConsole |
| ZLoggerFileLoggerProvider | ZLoggerFile | AddZLoggerFile |
| ZLoggerRollingFileLoggerProvider | ZLoggerRollingFile | AddZLoggerRollingFile |
| ZLoggerStreamLoggerProvider | ZLoggerStream | AddZLoggerStream |
| ZLoggerInMemoryProcessorLoggerProvider | ZLoggerInMemory | AddZLoggerInMemory |
| ZLoggerLogProcessorLoggerProvider | ZLoggerLogProcessor | AddZLoggerLogProcessor |
All Providers can take an Action that sets ZLoggerOptions as the last argument. As follows.
builder.Logging
.ClearProviders()
// Configure options
.AddZLoggerConsole(options =>
{
options.LogToStandardErrorThreshold = LogLevel.Error;
});
// Configure options with service provider
.AddZLoggerConsole((options, services) =>
{
options.TimeProvider = services.GetService<YourCustomTimeProvider>();
});
If you are using Microsoft.Extensions.Configuration, you can set the log level through configuration. In this case, alias of Provider can be used. for example:
{
"Logging": {
"LogLevel": {
"Default": "Information"
},
"ZLoggerConsoleLoggerProvider": {
"LogLevel": {
"Default": "Debug"
}
}
}
}
Each Provider's behavior can be modified using the common ZLoggerOptions. For details, please refer to the ZLoggerOptions section. Additionally, you can customize structured logging (JSON Logging) using the UseFormatter method within these options. For more information on this, check the Formatter Configurations section.
Console writes to the standard output. Console output is not only for development purposes, but also serves as a standard log input port in containerized and cloud environments, making performance critically important. ZLogger has been optimized to maximize console output performance.
logging.AddZLoggerConsole();
If you are using ZLoggerConsoleLoggerProvider, the following additional options are available:
| Name | Description |
|---|---|
bool OutputEncodingToUtf8 |
Set Console.OutputEncoding = new UTF8Encoding(false) when the provider is created. (default: true) |
bool ConfigureEnableAnsiEscapeCode |
If set true, then configure console option on execution and enable virtual terminal processing(enable ANSI escape code). (default: false) |
LogLevel LogToStandardErrorThreshold |
If set, logs at a higher level than the value will be output to standard error. (default: LogLevel.None) |
For cases where stdout is used for data input/output, such as MCP (model-context-protocol) servers, logs need to be directed to stderr. In such cases, LogToStandardErrorThreshold can be configured as follows:
var logger = Microsoft.Extensions.Logging.LoggerFactory.Create(x => {
// Configure all logs to go to stderr
x.AddZLoggerConsole(x => x.LogToStandardErrorThreshold = Microsoft.Extensions.Logging.LogLevel.Trace);
});
File outputs text logs to a file. This is a Provider that writes to a single file in append mode at high speed.
logging.AddZLoggerFile("log.txt");
If you are using ZLoggerFileLoggerProvider, the following additional options are available:
| Name | Description |
|---|---|
| `bool f |
No open issues yet, or sync has not completed.