It uses strongly-typed delegates to log messages, which means no more messing around with string manipulating at runtime. It’s like having a pre-baked log template that’s ready to go whenever you need it.

A Simple Comparison with Benchmark

I tested two ways to log 1M user IDs.

Why Is It So Fast?

It uses a reusable Action, so it just plugs in the values and logs the message. While there's still a small amount of work to insert the values, it's drastically faster than full string formatting. No extra work, no extra memory.

Some Pitfalls and Considerations

public static class LoggerMessageTemplates
{
    // A reusable logging delegate for informational logs
    private static readonly Action<ILogger, int, Exception> _logInfo =
        LoggerMessage.Define<int>(
            LogLevel.Information,
            new EventId(1, "LoggerMessageTemplates"),
            "Processing user from template: {UserId}");

    // Helper method for informational logs
    public static void LogInfo(this ILogger logger, int userId)
    {
        _logInfo(logger, userId, null!); // For info, pass null for the exception parameter
    }
}

Benchmark Code & Result

[Benchmark(Baseline = true)]

public Task ILogger()
{
	for (int userId = 1; userId <= 1000000; userId++)
	{
		_logger.LogInformation("Processing user {userId}", userId);
	}
	return Task.CompletedTask;
}
[Benchmark]
public Task LoggerMessage()
{
	for (int userId = 1; userId <= 1000000; userId++)
	{
		_logger.LogInfo(userId);
	}
	return Task.CompletedTask;
}

Output