惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
爱范儿
爱范儿
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
博客园 - 聂微东
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
T
Tailwind CSS Blog
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 司徒正美
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
罗磊的独立博客
Jina AI
Jina AI

博客园 - Charltsing

C# 预处理器指令语言 - 完整语法速查 PDFImageViewer v2.1 PDF查看图像和无损导出工具,一键转换单图PDF为JBig2黑白PDF。兼谈PDF内嵌图像。 PDF的色彩空间 25 个使用.NET 10 的性能技巧 PDFontFixer v1.5 免费版,解决PDF文档复制粘贴乱码的问题,修复PDF文档字体的Unicode映射。 为什么 PDF 编辑这么难? Unicode、UTF-8、UTF-16 如何直接编辑Github的Readme.md文件 自动导入工程项目属性,Directory.Build.props 和 Directory.Build.targets 新版 C# 高效率编程指南 C# 内联数组(Inline Array):高性能数组的新选择 如何在 .NET 中使用 SIMD .NET异步编程进阶:从语法糖到高性能架构的核心突破 C# 中的安全零拷贝 使用UnsafeAccessor 访问私有字段 C# 12与.NET 8实战指南:20个提升代码质量的最佳实践 C# Net9的模块初始化器(Module Initializer) ECMA-335 CLI 规范附录 C#中避免GC压力和提高性能的8种技术 函数内联 硬件内在函数 官方:oPDF v2.1免费版,专业的PDF水印分析处理工具,无损去除水印,通杀八类PDF水印。它是PDFCommander 万能PDF水印删除工具的升级版。 C#自动引用Debug | Release版本的dll
LoggerMessage:编写高性能的 .NET 日志
Charltsing · 2026-06-17 · via 博客园 - Charltsing

LoggerMessage 的四大性能法宝

LoggerMessage(尤其是结合 .NET 6+ 的源代码生成器)专门为了解决上述痛点而生。它通过以下四个核心机制,实现了极高性能和零内存分配(Zero Allocation)

1. 预解析并缓存模板

LoggerMessage 会在底层将模板的解析工作提前。字符串模板只会在类加载时被解析一次,并将结果缓存在静态字段中。后续每次记录该日志时,直接复用缓存,彻底干掉重复解析的 CPU 开销。

2. 强类型泛型委托(拒绝装箱)

LoggerMessage 使用预生成的泛型委托(例如 Action<ILogger, int, string, Exception?>)来处理参数。因为明确指出了 orderIdint 类型,参数传递时不会发生任何装箱操作。

3. 消灭 params object[] 数组

由于直接调用的是强类型委托,参数被直接塞进方法调用中,底层的 object[] 数组分配也被完美避开。

4. 前置的日志级别检查

生成的委托会在执行任何逻辑之前,第一时间检查 _logger.IsEnabled(LogLevel)。如果级别未启用,直接 return,实现零浪费。


🛠️ 现代玩法:使用源代码生成器 (.NET 6+)

在以前,手动编写 LoggerMessage.Define 需要写一堆繁琐的样板代码。从 .NET 6 开始,微软引入了 [LoggerMessage] 特性,配合编译器 Source Generators(源代码生成器),让你只需声明方法签名,剩下的全由编译器代劳。

你的代码(极简且优雅):

public partialclassOrderProcessor
{
    privatereadonly ILogger _logger;

    public OrderProcessor(ILogger<OrderProcessor> logger) => _logger = logger;

    // 1. 加上 [LoggerMessage] 特性
    // 2. 声明为 partial 方法
    [LoggerMessage(
        EventId = 1, 
        Level = LogLevel.Information, 
        Message = "Processing order {OrderId} for user {UserId}")]
    public partial void LogOrderProcessing(int orderId, string userId);

    public void Process(int orderId, string userId)
    {
        // 像普通方法一样调用,享受极高的性能
        LogOrderProcessing(orderId, userId); 
    }
}

 ********************

.NET 9 中的 LoggerMessage 源码生成器

LoggerMessage 是 .NET 6 引入的编译时日志源码生成特性,在 .NET 9 中持续演进,是目前 .NET 中高性能日志记录的推荐方案。


核心概念

[LoggerMessage] 特性通过 Source Generator 在编译时自动生成高效的日志方法,底层调用 LoggerMessage.Define() 缓存委托,避免运行时字符串拼接和装箱开销。


基本用法

1. 扩展方法方式(推荐)

public static partial class MyServiceLogging
{
    [LoggerMessage(
        EventId = 1000,
        Level = LogLevel.Information,
        Message = "用户 {UserId} 在 {LoginTime:yyyy-MM-dd HH:mm:ss} 登录")]
    public static partial void UserLoggedIn(this ILogger logger, int userId, DateTime loginTime);

    [LoggerMessage(
        EventId = 1001,
        Level = LogLevel.Error,
        Message = "处理订单 {OrderId} 失败")]
    public static partial void OrderProcessingFailed(this ILogger logger, Exception ex, Guid orderId);
}

 使用:

public class MyService(ILogger<MyService> logger)
{
    public void DoWork(int userId)
    {
        logger.UserLoggedIn(userId, DateTime.Now);
    }
}

2.  实例方法方式(依赖注入)

public partial class MyService
{
    private readonly ILogger<MyService> _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    [LoggerMessage(
        EventId = 0,
        Level = LogLevel.Information,
        Message = "PrintMessage: {message}")]
    public partial void LogPrintMessage(string message);
}

3.  动态日志级别

[LoggerMessage(
    EventId = 0,
    Message = "PrintMessage: {message}")]
public static partial void LogPrintMessage(this ILogger logger, LogLevel logLevel, string message);

.NET 9 的重要改进

改进项说明
主构造函数支持 .NET 9 修复了 LoggerMessage 与 C# 12 主构造函数不兼容的问题
更简洁的 API EventId 不再是必需参数,可直接写 [LoggerMessage(LogLevel.Warning, "message")]
属性支持 修复了使用 ILogger 属性(而非字段)时源码生成器崩溃的问题

约束条件

使用 LoggerMessage 时必须遵守以下规则:

  • 日志方法必须是 partial 且返回 void

  • 方法名不能以 _ 开头

  • 参数名不能以 _ 开头

  • 日志方法不能是泛型方法

  • 如果是 static 方法,ILogger 实例必须作为参数传入


生成的代码示例

编译器会自动生成类似下面的代码:

[GeneratedCode("Microsoft.Extensions.Logging.Generators", "9.0.12.6610")]
private static readonly Action<ILogger, int, DateTime, Exception?> __UserLoggedInCallback =
    LoggerMessage.Define<int, DateTime>(
        LogLevel.Information,
        new EventId(1000, "UserLoggedIn"),
        "Goat number {Id} logged in at {LoginTime}",
        new LogDefineOptions() { SkipEnabledCheck = true }
    );

[GeneratedCode("Microsoft.Extensions.Logging.Generators", "9.0.12.6610")]
public static void UserLoggedIn(this ILogger logger, int id, DateTime loginTime)
{
    if (!logger.IsEnabled(LogLevel.Information))
        return;
    __UserLoggedInCallback(logger, id, loginTime, null);
}

揭秘魔法:编译器为你生成的另一半代码

为什么上面那个没有方法体的 partial 方法能工作?如果你扒开编译器的幕后,你会发现它为你生成了这样一段高度优化的代码:

// <auto-generated/>
// 编译器自动生成的另一半 OrderProcessor 类

#nullable enable
publicpartialclassOrderProcessor
{
    // 静态缓存强类型委托,模板只解析一次!
    privatestaticreadonly Action<ILogger, int, string, Exception?> __LogOrderProcessingCallback =
        LoggerMessage.Define<int, string>(
            LogLevel.Information,
            new EventId(1, nameof(LogOrderProcessing)),
            "Processing order {OrderId} for user {UserId}",
            new LogDefineOptions() { SkipEnabledCheck = true }); 

    // 补全你的 partial 方法
    public partial void LogOrderProcessing(int orderId, string userId)
    {
        // 极致的前置性能检查
        if (_logger.IsEnabled(LogLevel.Information))
        {
            // 直接调用委托,传值类型 int,零装箱,零数组分配!
            __LogOrderProcessingCallback(_logger, orderId, userId, null);
        }
    }
}

看看这段生成的代码,我们前面提到的四大性能法宝(静态缓存、前置检查、强类型、无数组分配)在这里展现得淋漓尽致。


💡 总结

如果你的应用对性能要求极高,或者某个模块(如中间件、后台循环任务、高频接口)会产生海量的日志,请务必抛弃标准的扩展方法,拥抱 [LoggerMessage] 源代码生成器