Rougamo.Fody 实现一个AOP日志
使用 Rougamo.Fody 实现 AOP 日志,并定义一个 ILog
接口供 SDK 使用,具体日志实现由调用方提供。这是一个非常标准的解耦设计模式,适用于中间件、SDK、插件等场景。
✅ 整体架构目标
组件 | 说明 |
ILog | 定义日志接口,供内部使用 |
| 默认空实现,避免未注入时抛异常 |
| 基于 Rougamo.Fody 的切面类,用于方法拦截并记录日志 |
| 示例 类,展示如何在业务逻辑中使用日志 |
调用方 | 提供具体的日志实现(如 Serilog、NLog 等) |
📦 第一步:安装依赖包
bashdotnet add package Rougamo.Fody
安装后会自动创建 FodyWeavers.xml
文件,内容为 <Rougamo />
,确保存在。
🧩 第二步:定义 ILog
和默认实现
1. 定义接口
csharppublic interface ILog
{void Debug(string message, params object[] args);void Info(string message, params object[] args);void Warn(string message, params object[] args);void Error(Exception exception, string message, params object[] args);
}
2. 默认无输出实现(Null Object Pattern)
csharpinternal class NullLog : ILog
{public void Debug(string message, params object[] args) { }public void Info(string message, params object[] args) { }public void Warn(string message, params object[] args) { }public void Error(Exception exception, string message, params object[] args) { }
}
🛠️ 第三步:创建基于 Rougamo.Fody 的日志切面
csharpusing Rougamo;
using System;
using System.Reflection;[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class LogAttribute : MoAttribute
{private static ILog _log = new NullLog();public static void SetLogger(ILog logger){_log = logger ?? new NullLog();}public override void OnEntry(MethodBase method, object?[]? args){_log.Debug("Entering {DeclaringType}.{MethodName}({Parameters})",method.DeclaringType?.Name,method.Name,string.Join(", ", args ?? Array.Empty<object>()));}public override void OnExit(MethodBase method, object?[]? args, object? result){_log.Debug("Exiting {DeclaringType}.{MethodName}, 返回值: {Result}",method.DeclaringType?.Name,method.Name,result);}public override void OnException(MethodBase method, object?[]? args, Exception exception){_log.Error(exception, "异常 {DeclaringType}.{MethodName}: {Message}",method.DeclaringType?.Name,method.Name,exception.Message);}
}
📌 第四步:在 库中使用 [Log]
切面
示例 类:
csharp[Log]
public class MyService
{public int Divide(int a, int b){return a / b;}public void DoSomething(){Console.WriteLine("执行业务逻辑");}
}
🧪 第五步:调用方注入日志实现(例如使用 Serilog)
1. 安装 Serilog 包(可选)
bashdotnet add package Serilog
dotnet add package Serilog.Sinks.Console
2. 实现适配器
csharpusing Serilog;public class SerilogAdapter : ILog
{private readonly ILogger _logger;public SerilogAdapter(ILogger logger) => _logger = logger;public void Debug(string message, params object[] args) =>_logger.Debug(message, args);public void Info(string message, params object[] args) =>_logger.Information(message, args);public void Warn(string message, params object[] args) =>_logger.Warning(message, args);public void Error(Exception exception, string message, params object[] args) =>_logger.Error(exception, message, args);
}
3. 初始化日志并设置给切面
csharpvar serilogLogger = new LoggerConfiguration().WriteTo.Console().CreateLogger();// 注入日志实现到切面
LogAttribute.SetLogger(new SerilogAdapter(serilogLogger));// 测试调用
var service = new MyService();
service.Divide(10, 0); // 触发异常,日志应被记录
📌 总结
功能 | 实现方式 |
方法拦截 |
|
日志抽象接口 |
|
默认无日志实现 |
|
具体日志实现 | 调用方可注入(如 SerilogAdapter) |
支持类/方法标注 | ✅ 支持 |
注意
最后如果你准备使用肉夹馍,并且你准备使用肉夹馍开发一个供他人使用的NuGet组件,那么你需要把项目文件(.csproj)中Rougamo.Fody
的引用改成下面这样,不然你发布的NuGet其他人引用后将需要额外引用Fody,否则将无法进行代码织入
<PackageReference Include="Rougamo.Fody" Version="1.0.1" IncludeAssets="all" PrivateAssets="contentfiles;analyzers" /