A high-performance, zero-allocation logging library for .NET, Unity, and Godot with Native AOT support. Built for game development scenarios where performance is critical.
- Zero-Allocation Logging: Uses interpolated string handlers and
Span<char>to eliminate heap allocations - Native AOT Compatible: Fully supports ahead-of-time compilation for maximum performance
- Asynchronous File Writing: Non-blocking log writes using channels and background processing
- Automatic File Rotation: Manages log files with configurable limits and automatic cleanup
- Flexible Log Processing: Extensible processor system for custom log handling
- Custom Formatting: Full control over log output format via
ILogFormatterinterface - Unity Integration: Available as a Unity package
- Godot Integration: Available as a Godot addon
Install via NuGet:
dotnet add package GroveGames.Loggerusing GroveGames.Logger;
var logger = LoggerFactory.CreateLogger(builder =>
{
builder.SetMinimumLevel(LogLevel.Information)
.AddLogProcessor(new ConsoleLogProcessor(new ConsoleLogFormatter()));
});
logger.LogInformation("App", $"Application started at {DateTime.UtcNow}");
logger.LogWarning("App", $"Memory usage: {GC.GetTotalMemory(false) / 1024 / 1024}MB");
logger.LogError("App", $"Failed to load configuration");var logger = LoggerFactory.CreateLogger(builder =>
{
var logFileFactory = new LogFileFactory(
root: "logs",
folderName: "app",
maxFileCount: 10,
bufferSize: 8192
);
var fileStream = logFileFactory.CreateFile();
var streamWriter = new StreamWriter(fileStream, bufferSize: 8192, channelCapacity: 1000);
var fileProcessor = new FileLogProcessor(streamWriter, new FileLogFormatter());
builder.SetMinimumLevel(LogLevel.Debug)
.AddLogProcessor(fileProcessor);
});Implement ILogFormatter to create custom formats:
public sealed class JsonLogFormatter : ILogFormatter
{
public int GetBufferSize(LogLevel level, ReadOnlySpan<char> tag, ReadOnlySpan<char> message)
{
return 100 + tag.Length + message.Length;
}
public void Format(Span<char> buffer, LogLevel level, ReadOnlySpan<char> tag, ReadOnlySpan<char> message)
{
var timestamp = DateTime.UtcNow;
var position = 0;
"{\"time\":\"".AsSpan().CopyTo(buffer[position..]);
position += 10;
timestamp.TryFormat(buffer[position..], out var written, "O");
position += written;
// ... continue building JSON
}
}
var logger = LoggerFactory.CreateLogger(builder =>
{
builder.AddLogProcessor(new FileLogProcessor(streamWriter, new JsonLogFormatter()));
});The logger uses interpolated string handlers that only allocate when the log level is active:
for (int i = 0; i < 1000000; i++)
{
logger.LogDebug("Loop", $"Iteration {i} of {1000000}");
}ILogger: Core logging interface with minimal API surfaceLogger: Main implementation with processor managementLoggerFactory: Primary entry point for creating loggers with builder configurationLoggerBuilder: Fluent API for logger configurationILogProcessor: Interface for processing log entriesILogFormatter: Interface for controlling log entry formattingStreamWriter: High-performance async file writer with batchingLogFileFactory: Manages log file creation and rotation
There are two installation steps required to use it in Unity.
-
Install
GroveGames.Loggerfrom NuGet using NuGetForUnity. Open Window from NuGet → Manage NuGet Packages, search "GroveGames.Logger" and press Install. -
Install the
GroveGames.Logger.Unitypackage by referencing the git URL:https://github.com/grovegs/Logger.git?path=src/GroveGames.Logger.Unity/Packages/com.grovegames.logger -
Create a
csc.rspfile in yourAssets/directory with the following content to enable C# 10 features:-langversion:10 -nullable:enable
With the Unity package, Unity-specific formatters and processors become available for logging in Unity projects.
using GroveGames.Logger.Unity;
using UnityEngine;
public class GameManager : MonoBehaviour
{
private Logger _logger;
private void Awake()
{
_logger = UnityLoggerFactory.CreateLogger(builder =>
{
builder.AddUnityConsoleLogProcessor();
builder.AddUnityFileLogProcessor();
});
_logger.LogInformation("Game", $"Unity {Application.unityVersion} initialized");
}
private void OnDestroy()
{
_logger?.Dispose();
}
}Configure logger settings via Edit → Project Settings → GroveGames → Logger. Settings are stored as a ScriptableObject in Assets/Settings/GroveGamesLoggerSettings.asset and automatically included in builds via EditorBuildSettings.
| Setting | Type | Default | Description |
|---|---|---|---|
Min Log Level |
LogLevel |
Information |
Minimum level for log output |
Max File Count |
int |
10 |
Maximum number of log files to retain |
File Folder Name |
string |
"logs" |
Folder name for log files |
File Buffer Size |
int |
8192 |
Buffer size in bytes for file operations |
File Channel Capacity |
int |
1000 |
Channel capacity for async log processing |
For custom configurations or DI scenarios, pass settings directly:
var settings = UnityLoggerSettings.GetOrCreate();
var logger = UnityLoggerFactory.CreateLogger(settings, builder => { ... });UnityLoggerFactory: Factory with GetOrCreate() pattern for DI-friendly architectureUnityLoggerSettings: ScriptableObject with EditorBuildSettings integrationUnityLoggerSettingsProvider: Project Settings UI using UI ToolkitUnityConsoleLogFormatter: Formatter for Unity console output (format:[Tag] Message)UnityConsoleLogProcessor: Routes logs toDebug.Log,Debug.LogWarning,Debug.LogErrorUnityLogFileFactory: File factory usingApplication.persistentDataPathUnityLogHandler: Captures Unity's internal logs and forwards them to file logging
Install via NuGet:
dotnet add package GroveGames.Logger.GodotDownload the Godot addon from the latest release and extract it to your project's addons folder. Enable the addon in Project Settings → Plugins.
res://
├── addons/
│ └── GroveGames.Logger/
│ ├── plugin.cfg
│ └── ...
└── ...
using GroveGames.Logger;
using Godot;
public partial class Main : Node
{
private Logger _logger;
public override void _Ready()
{
_logger = GodotLoggerFactory.CreateLogger(builder =>
{
builder.AddGodotConsoleLogProcessor();
builder.AddGodotFileLogProcessor();
});
_logger.LogInformation("Game", $"Godot {Engine.GetVersionInfo()} initialized");
}
public override void _ExitTree()
{
_logger?.Dispose();
}
}The GodotConsoleLogFormatter provides rich formatting for the Godot editor console:
- Warning messages are highlighted with yellow color
- Timestamps in HH:mm:ss format for easy debugging
- Clean tag presentation with brackets
Configure logger settings via a Resource stored at res://addons/GroveGames.Logger/LoggerSettings.tres. The resource path is registered in project settings and automatically included in builds.
| Setting | Type | Default | Description |
|---|---|---|---|
Min Log Level |
LogLevel |
Information |
Minimum level for log output |
Max File Count |
int |
10 |
Maximum number of log files to retain |
File Folder Name |
string |
"logs" |
Folder name for log files |
File Buffer Size |
int |
8192 |
Buffer size in bytes for file operations |
File Channel Capacity |
int |
1000 |
Channel capacity for async log processing |
For custom configurations or DI scenarios, reference the settings resource:
public partial class GameManager : Node
{
[Export] private GodotLoggerSettingsResource _loggerSettings;
public override void _Ready()
{
var logger = GodotLoggerFactory.CreateLogger(_loggerSettings, builder => { ... });
}
}GodotLoggerFactory: Factory with GetOrCreate() pattern for DI-friendly architectureGodotLoggerSettingsResource: Resource with ProjectSettings integrationGodotConsoleLogFormatter: Rich formatting for Godot's editor consoleGodotConsoleLogProcessor: Processor optimized for Godot's output methodsGodotLogFileFactory: File factory usingOS.GetUserDataDir()
- Zero-Allocation String Handling: Uses
Span<char>and stackalloc throughout - Interpolated String Handlers: Compile-time optimization for string formatting
- Channel-Based Async I/O: Lock-free producer-consumer pattern for file writes
- Batched Writing: Reduces I/O operations by batching multiple log entries
- Native AOT: Full compatibility with ahead-of-time compilation
Run tests:
dotnet testContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.