寻求低侵入性方法执行日志类库:仅需添加Decorator/Attribute
兄弟,完全有这种低侵入性的方案!我之前也踩过侵入性强的日志类库的坑——要么得改一堆业务代码,要么耦合度高到后期维护头疼。用Attribute+AOP的思路完美匹配你的需求,只需要给目标方法加个特性,就能自动记录方法执行信息,完全不碰业务逻辑本身。
下面给你两种方向的解决方案,按需选就行:
一、现成类库(省心省力,零侵入)
1. PostSharp
这是老牌的.NET AOP框架,用特性就能实现方法拦截,简直为你的需求量身定做。你只需要定义一个继承自OnMethodBoundaryAspect的特性,实现OnEntry(方法进入)、OnExit(方法退出)等钩子方法,然后给需要监控的方法贴个标签就行。
示例代码:
using PostSharp.Aspects; [Serializable] public class MethodLoggingAttribute : OnMethodBoundaryAspect { // 方法开始执行时触发 public override void OnEntry(MethodExecutionArgs args) { var paramStr = string.Join(", ", args.Arguments.Select(a => a?.ToString() ?? "null")); Console.WriteLine($"[日志] 方法 {args.Method.Name} 开始执行,参数:{paramStr}"); } // 方法执行完成时触发 public override void OnExit(MethodExecutionArgs args) { Console.WriteLine($"[日志] 方法 {args.Method.Name} 执行完成,返回值:{args.ReturnValue}"); } // 方法抛出异常时触发 public override void OnException(MethodExecutionArgs args) { Console.WriteLine($"[日志] 方法 {args.Method.Name} 抛出异常:{args.Exception.Message}"); } } internal class Calculator { // 只需要加这个特性就搞定! [MethodLogging] public static int Add(int a, int b) { return a + b; } }
PostSharp免费版就能覆盖基础的日志需求,付费版有更多高级功能(比如性能监控、事务管理),看你项目规模选就行。
2. Fody + MethodDecorator.Fody
Fody是通过编译时IL织入实现AOP的工具,没有运行时代理的性能损耗,非常轻量化。MethodDecorator.Fody是它的一个专门做方法拦截的插件,用法同样简单到离谱:
- 先安装NuGet包:
MethodDecorator.Fody - 定义你的拦截特性:
using System.Reflection; [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class CustomInterceptorAttribute : Attribute { // 方法进入时调用 public void OnEntry(object instance, MethodBase method, object[] args) { var paramStr = string.Join(", ", args.Select(a => a?.ToString() ?? "null")); Console.WriteLine($"[拦截] 进入方法:{method.Name},参数:{paramStr}"); } // 方法退出时调用(含返回值) public void OnExit(object instance, MethodBase method, object[] args, object returnValue) { Console.WriteLine($"[拦截] 退出方法:{method.Name},返回值:{returnValue}"); } // 方法抛出异常时调用 public void OnException(object instance, MethodBase method, object[] args, Exception exception) { Console.WriteLine($"[拦截] 方法 {method.Name} 异常:{exception.Message}"); } } internal class Calculator { // 同样只需要加这个特性 [CustomInterceptor] public static int Add(int a, int b) { return a + b; } }
Fody是编译阶段修改IL,运行时完全和原生代码一样,性能拉满,而且完全符合你“只加Attribute”的要求。
二、自己实现轻量版(无第三方依赖,灵活可控)
如果你不想依赖任何第三方库,用.NET自带的DispatchProxy就能实现动态代理(注意:默认只支持实例方法,静态方法需要额外处理)。
步骤如下:
- 先定义标记用的特性:
[AttributeUsage(AttributeTargets.Method)] public class CustomInterceptorAttribute : Attribute { // 空特性,只做标记用 }
- 实现代理类,处理拦截逻辑:
using System.Reflection; using System.Reflection.DispatchProxy; public class LoggingProxy<T> : DispatchProxy { private T _targetInstance; // 核心拦截逻辑 protected override object Invoke(MethodInfo targetMethod, object[] args) { var hasInterceptor = targetMethod.GetCustomAttribute<CustomInterceptorAttribute>() != null; if (hasInterceptor) { var paramStr = string.Join(", ", args.Select(a => a?.ToString() ?? "null")); Console.WriteLine($"[自定义拦截] 方法 {targetMethod.Name} 开始执行,参数:{paramStr}"); } object result = null; try { // 执行原方法 result = targetMethod.Invoke(_targetInstance, args); if (hasInterceptor) { Console.WriteLine($"[自定义拦截] 方法 {targetMethod.Name} 执行完成,返回值:{result}"); } return result; } catch (Exception ex) { if (hasInterceptor) { var realEx = ex.InnerException ?? ex; Console.WriteLine($"[自定义拦截] 方法 {targetMethod.Name} 抛出异常:{realEx.Message}"); } throw; } } // 创建代理实例的工厂方法 public static T Create(T target) { var proxy = Create<T, LoggingProxy<T>>(); ((LoggingProxy<T>)proxy)._targetInstance = target; return (T)proxy; } }
- 使用方式(把静态方法改成实例方法即可):
internal class Calculator { [CustomInterceptor] public int Add(int a, int b) { return a + b; } } // 测试代码 var calculator = LoggingProxy<Calculator>.Create(new Calculator()); var sum = calculator.Add(5, 7);
如果一定要支持静态方法,可以用Reflection.Emit生成动态类或者结合Roslyn编译,不过相对复杂一些,上面的实例方案足够覆盖绝大多数业务场景了。
内容的提问来源于stack exchange,提问作者Juan Simon
相关产品推荐
相关产品推荐

