如何在不修改接口所在DLL的前提下,统一为Handle()方法添加日志?
可行方案(无需修改原DLL)
完全可以在不修改原DLL和现有149个Handle方法的前提下实现统一日志注入,以下是几种实用方案:
1. 装饰器模式(无第三方依赖)
实现一个泛型装饰器类,包装所有IMessageHandler<TMessage>的实现,统一在Handle方法前添加日志逻辑:
步骤1:定义泛型装饰器
public class LoggingMessageHandler<TMessage> : IMessageHandler<TMessage> { private readonly IMessageHandler<TMessage> _targetHandler; private readonly ILogger _log; public LoggingMessageHandler(IMessageHandler<TMessage> targetHandler, ILogger log) { _targetHandler = targetHandler; _log = log; } public void Handle(TMessage message) { // 统一添加日志 _log.Debug($"Handling [{message.GetType().Name}]"); // 调用原Handle方法 _targetHandler.Handle(message); } }
步骤2:替换原有实例
将原来直接创建的GeneralManager、SpecificManager实例,替换为装饰后的实例:
// 原代码:var manager = new GeneralManager(); var rawManager = new GeneralManager(); var loggingManager = new LoggingMessageHandler<GetStockMessage>(rawManager, log); // 如果是依赖注入环境(比如ASP.NET Core),可以批量注册: services.AddScoped(typeof(IMessageHandler<>), typeof(LoggingMessageHandler<>)); // 再注册实际的实现类 services.AddScoped<IMessageHandler<GetStockMessage>, GeneralManager>(); services.AddScoped<IMessageHandler<GetListMessage>, SpecificManager>(); // 注:部分DI容器(如Autofac)有专门的AddDecorator方法,更方便批量装饰
2. AOP切面框架(最省心)
使用AOP框架(如PostSharp、Castle DynamicProxy)通过切面拦截所有Handle方法,自动注入日志:
示例:PostSharp实现
- 定义日志切面特性:
[Serializable] public class LogMessageHandlingAttribute : OnMethodBoundaryAspect { public override void OnEntry(MethodExecutionArgs args) { // 仅拦截IMessageHandler的Handle方法 if (args.Method.Name == nameof(IMessageHandler<object>.Handle) && args.Arguments.Length > 0) { var message = args.Arguments[0]; var log = LogManager.GetLogger(args.Method.DeclaringType); log.Debug($"Handling [{message.GetType().Name}]"); } } }
- 全局应用切面:
可以直接给所有实现IMessageHandler<T>的类加[LogMessageHandling]特性,或者通过PostSharp的全局配置,自动对所有匹配的Handle方法应用该切面,无需逐个修改类。
示例:Castle DynamicProxy实现
- 定义拦截器:
public class LoggingInterceptor : IInterceptor { private readonly ILogger _log; public LoggingInterceptor(ILogger log) { _log = log; } public void Intercept(IInvocation invocation) { // 拦截Handle方法 if (invocation.Method.Name == nameof(IMessageHandler<object>.Handle) && invocation.Arguments.Any()) { var message = invocation.Arguments[0]; _log.Debug($"Handling [{message.GetType().Name}]"); } // 执行原方法 invocation.Proceed(); } }
- 生成代理实例:
var proxyGenerator = new ProxyGenerator(); // 给GeneralManager生成代理,自动拦截所有Handle方法 var proxiedManager = proxyGenerator.CreateClassProxy<GeneralManager>(new LoggingInterceptor(log));
3. 反射+动态代码生成(适合特殊场景)
如果无法修改实例创建逻辑,也可以通过反射获取所有实现IMessageHandler<T>的类,使用Roslyn或Emit动态生成带日志的包装类,替换原有类型的实例。这种方式复杂度较高,适合不能用前两种方案的极端场景。
内容的提问来源于stack exchange,提问作者Dominique
相关产品推荐
相关产品推荐

