AutoLog.Generator
1.0.1
dotnet add package AutoLog.Generator --version 1.0.1
NuGet\Install-Package AutoLog.Generator -Version 1.0.1
<PackageReference Include="AutoLog.Generator" Version="1.0.1" />
<PackageVersion Include="AutoLog.Generator" Version="1.0.1" />
<PackageReference Include="AutoLog.Generator" />
paket add AutoLog.Generator --version 1.0.1
#r "nuget: AutoLog.Generator, 1.0.1"
#:package AutoLog.Generator@1.0.1
#addin nuget:?package=AutoLog.Generator&version=1.0.1
#tool nuget:?package=AutoLog.Generator&version=1.0.1
AutoLog.Generator
AutoLog turns simple [Log]-annotated partial methods into high-performance LoggerMessage.Define logging at build time. You keep normal ILogger fields and readable structured log messages; the generator emits cached delegates, EventIds, and partial method bodies for you.
Why AutoLog?
Microsoft's built-in [LoggerMessage] pattern is fast, but it pushes a lot of ceremony into your code:
static partialmethods- explicit logger parameters
- manual event IDs
- more boilerplate around every log statement
AutoLog keeps the performance benefits while simplifying the authoring model:
- Just add
[Log]to apartial voidmethod - Use your existing
ILoggerfield or property - Auto-detects
ILogger/ILogger<T> - Auto-assigns
EventIds per class - AOT-safe and zero-reflection
Installation
dotnet add package AutoLog.Generator
Then add a normal ILogger field or property to your partial class.
Quick start
using AutoLog;
using Microsoft.Extensions.Logging;
public partial class OrderService
{
private readonly ILogger<OrderService> _logger;
public OrderService(ILogger<OrderService> logger) => _logger = logger;
[Log(LogLevel.Information, "Processing order {OrderId} for customer {CustomerId}")]
partial void LogProcessingOrder(int orderId, string customerId);
[Log(LogLevel.Warning, "Order {OrderId} not found")]
partial void LogOrderNotFound(int orderId);
[Log(LogLevel.Error, "Failed to process order {OrderId}")]
partial void LogProcessingFailed(int orderId, Exception ex);
}
AutoLog generates code like:
// <auto-generated by AutoLog.Generator/>
#nullable enable
partial class OrderService
{
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, int, string, global::System.Exception?> _logProcessingOrderAction =
global::Microsoft.Extensions.Logging.LoggerMessage.Define<int, string>(
global::Microsoft.Extensions.Logging.LogLevel.Information,
new global::Microsoft.Extensions.Logging.EventId(1, "LogProcessingOrder"),
"Processing order {OrderId} for customer {CustomerId}");
partial void LogProcessingOrder(int orderId, string customerId)
=> _logProcessingOrderAction(_logger, orderId, customerId, null);
}
Comparison with Microsoft's [LoggerMessage]
[LoggerMessage]
using Microsoft.Extensions.Logging;
public static partial class OrderLogs
{
[LoggerMessage(
EventId = 1001,
Level = LogLevel.Information,
Message = "Processing order {OrderId} for customer {CustomerId}")]
public static partial void ProcessingOrder(
ILogger logger,
int orderId,
string customerId);
}
AutoLog
using AutoLog;
using Microsoft.Extensions.Logging;
public partial class OrderService
{
private readonly ILogger<OrderService> _logger;
[Log(LogLevel.Information, "Processing order {OrderId} for customer {CustomerId}")]
partial void LogProcessingOrder(int orderId, string customerId);
}
Why it feels simpler
| Concern | [LoggerMessage] |
AutoLog |
|---|---|---|
| Method shape | static partial |
instance partial void |
| Logger parameter | explicit in every method | auto-detected from class |
| Event IDs | manually managed | sequential per class |
| Boilerplate | medium | low |
| Generated performance | excellent | excellent |
Exception parameter handling
If the last parameter is Exception, AutoLog maps it to the exception slot of LoggerMessage.Define instead of treating it as a template parameter:
[Log(LogLevel.Error, "Failed to process order {OrderId}")]
partial void LogProcessingFailed(int orderId, Exception ex);
Generates:
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, int, global::System.Exception?> _logProcessingFailedAction =
global::Microsoft.Extensions.Logging.LoggerMessage.Define<int>(
global::Microsoft.Extensions.Logging.LogLevel.Error,
new global::Microsoft.Extensions.Logging.EventId(1, "LogProcessingFailed"),
"Failed to process order {OrderId}");
partial void LogProcessingFailed(int orderId, Exception ex)
=> _logProcessingFailedAction(_logger, orderId, ex);
Rules
[Log]only works onpartial voidmethods- The method must live on a
partial class - The containing class must expose an instance
ILoggerorILogger<T>field/property - AutoLog uses the first matching logger member it finds
LoggerMessage.Define<T1..T6>supports up to 6 non-exception parameters- If a log method exceeds 6 parameters, AutoLog emits AL003 and falls back to an object-array logging path
Diagnostics
| Code | Severity | Message |
|---|---|---|
| AL001 | Error | [Log] on {Method} in {Class} — method must be partial void. |
| AL002 | Error | [Log] on {Method} — containing class {Class} has no ILogger or ILogger<T> field or property. Add one to enable log generation. |
| AL003 | Warning | [Log] on {Method} has {N} type parameters — LoggerMessage.Define supports a maximum of 6. |
Migrating from [LoggerMessage]
Microsoft's source-generated [LoggerMessage] and AutoLog produce the same LoggerMessage.Define output. The difference is ergonomics.
Before ([LoggerMessage])
public static partial class OrderLogs
{
[LoggerMessage(EventId = 1001, Level = LogLevel.Information,
Message = "Processing order {OrderId} for customer {CustomerId}")]
public static partial void ProcessingOrder(ILogger logger, int orderId, string customerId);
[LoggerMessage(EventId = 1002, Level = LogLevel.Warning,
Message = "Order {OrderId} not found")]
public static partial void OrderNotFound(ILogger logger, int orderId);
}
// Call site — must pass logger explicitly every time:
OrderLogs.ProcessingOrder(_logger, order.Id, order.CustomerId);
After (AutoLog)
public partial class OrderService
{
private readonly ILogger<OrderService> _logger;
[Log(LogLevel.Information, "Processing order {OrderId} for customer {CustomerId}")]
partial void LogProcessingOrder(int orderId, string customerId);
[Log(LogLevel.Warning, "Order {OrderId} not found")]
partial void LogOrderNotFound(int orderId);
}
// Call site — logger is implicit:
LogProcessingOrder(order.Id, order.CustomerId);
Migration steps
- Install
AutoLog.Generator—dotnet add package AutoLog.Generator - Make your service class
partial - Replace
static partiallog methods withpartial voidinstance methods and[Log] - Remove the explicit
ILoggerparameter — AutoLog detects it from the field - Remove manual
EventIds — auto-assigned sequentially per class - Update call sites — drop
ClassName.prefix and the logger argument
Also by the same author
🌐 Full suite overview: swevo.github.io
| Package | Description |
|---|---|
| AutoHttpClient.Generator | Compile-time typed HTTP client — [HttpClient] on an interface generates a strongly-typed client. AOT-safe Refit alternative. |
| AutoDispatch.Generator | Compile-time CQRS dispatcher — [Handler] generates a strongly-typed IDispatcher. No MediatR, no reflection. |
| AutoWire | Compile-time DI auto-registration — [Scoped]/[Singleton]/[Transient] generates IServiceCollection registration code. |
| AutoMap.Generator | Compile-time object mapping with generated extension methods. AOT-safe AutoMapper alternative. |
| AutoValidate.Generator | Compile-time FluentValidation wiring — discovers validators and generates AddValidators(). |
| AutoResult.Generator | Compile-time Result<T> — [TryWrap] generates Try*() wrappers for every public method. |
| AutoQuery.Generator | Compile-time LINQ query specs — [QuerySpec] generates a strongly-typed Apply(IQueryable<T>). |
License
MIT
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.0.0: [Log(Level, Message)] on partial void methods; LoggerMessage.Define generation; ILogger field auto-detection; EventId auto-assignment; AL001/AL002/AL003 diagnostics.