如何捕获ILogger.LogCritical调用并在调试器中触发断点?
如何让ILogger.LogCritical()在调试时自动触发断点
完全可以实现!而且不需要替换整个底层日志库,也不用让开发者改调用方式——核心思路是用装饰器模式包装原生的ILogger实例,在不改变原有调用逻辑的前提下,插入断点触发的逻辑。
具体实现步骤
1. 写一个Logger装饰器类
这个类会包装原生的ILogger<T>,在Log方法里(所有扩展方法比如LogCritical最终都会调用这个方法)判断日志级别和调试状态,触发断点后再转发调用给原生Logger:
using System.Diagnostics; using Microsoft.Extensions.Logging; public class DebugBreakLogger<T> : ILogger<T> { private readonly ILogger<T> _originalLogger; public DebugBreakLogger(ILogger<T> originalLogger) { _originalLogger = originalLogger; } public IDisposable BeginScope<TState>(TState state) => _originalLogger.BeginScope(state); public bool IsEnabled(LogLevel logLevel) => _originalLogger.IsEnabled(logLevel); public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { // 仅当日志级别为Critical且调试器已附加时触发断点 if (logLevel == LogLevel.Critical && Debugger.IsAttached) { Debugger.Break(); // 会在当前调试器中暂停程序 // 如果需要启动新调试器,可以用 Debugger.Launch() } // 继续执行原生的日志记录逻辑 _originalLogger.Log(logLevel, eventId, state, exception, formatter); } }
2. 替换DI容器中的Logger注册
在你的Program.cs(或者Startup.cs)里,把原生的ILogger<T>注册替换成我们的装饰器。这样所有依赖注入ILogger<T>的地方,都会拿到包装后的实例:
var builder = WebApplication.CreateBuilder(args); // 保留原有的日志配置(比如AddConsole、AddSerilog等) builder.Services.AddLogging(); // 替换所有ILogger<T>的实现为我们的装饰器 builder.Services.Replace(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(DebugBreakLogger<>))); // ... 其他服务注册和应用启动逻辑
为什么这个方案满足你的需求
- 无需修改现有代码:开发者还是像之前一样调用
_logger.LogCritical(),完全不用区分原生还是自定义方法,也不用改命名空间。 - 不绑定底层日志库:不管你用的是原生ConsoleLogger、Serilog还是NLog,只要是基于
Microsoft.Extensions.Logging抽象的,这个逻辑都能生效,未来换日志库也不用改动这部分代码。 - 仅调试环境生效:通过
Debugger.IsAttached判断,生产环境不会触发断点,完全不影响线上运行。 - 实现成本极低:只需要一个几十行的装饰器类和一行注册代码,不用实现一堆繁琐的日志接口。
额外注意点
- 确保引用了
System.Diagnostics命名空间,否则Debugger类会找不到。 - 如果你的项目中有用到直接实例化的
ILogger(不是通过DI注入的),那这种情况需要单独处理,但绝大多数.NET项目都是用DI注入ILogger<T>的,所以这个方案覆盖了绝大多数场景。
内容的提问来源于stack exchange,提问作者ChrisTorng
相关产品推荐
相关产品推荐

