当前位置: 首页 > news >正文

ABP VNext + Fody AOP:编译期织入与性能监控

ABP VNext + Fody AOP:编译期织入与性能监控 🚀


📚 目录

  • ABP VNext + Fody AOP:编译期织入与性能监控 🚀
    • 1. 引言 💡
    • 2. 环境与依赖 🛠
    • 3. 启用 Fody(项目配置) ⚙️
      • 📊 编译期织入流程
    • 4. 编译期切面 Attribute 🔍
      • 日志 + 性能计时 + 异常捕获
      • 📊 方法调用运行流程
    • 5. 应用范围 🎯
    • 6. Serilog 接入 📜
    • 7. 阈值配置化 🎚
    • 8. Benchmark 测试 🏎
    • 9. 常见问题与规避 ⚠


1. 引言 💡

TL;DR
Fody + MethodDecorator.Fody编译期为 ABP ApplicationService 自动注入:日志、耗时计时、异常捕获。

  • 极少反射/零侵入:仅在切面初始化时使用少量反射获取方法信息;不依赖运行时动态代理。
  • 🛠 支持 同步/异步 方法;可与 ABP 模块化/DI 并存。
  • 📊 附 BenchmarkDotNet 对比,量化织入开销。

为什么不是运行时 AOP?
运行时代理(Castle DynamicProxy)会引入启动延迟、调用额外开销,还可能和第三方拦截器冲突;编译期 IL 织入更轻、更可控。


2. 环境与依赖 🛠

  • 平台:.NET 6.x、ABP v6.x

  • NuGet 包

    dotnet add package Fody
    dotnet add package MethodDecorator.Fody
    dotnet add package Serilog
    dotnet add package Serilog.Extensions.Hosting
    dotnet add package Serilog.Sinks.Console
    dotnet add package BenchmarkDotNet
    
  • 注意

    • 📂 FodyWeavers.xml 放在项目根目录(与 .csproj 同级)。

    • 推荐 .csproj 配置:

      <PropertyGroup><LangVersion>latest</LangVersion><Nullable>enable</Nullable>
      </PropertyGroup><ItemGroup><PackageReference Include="Fody" Version="*" PrivateAssets="all" /><PackageReference Include="MethodDecorator.Fody" Version="*" PrivateAssets="all" />
      </ItemGroup>
      

3. 启用 Fody(项目配置) ⚙️

FodyWeavers.xml 示例(限制织入范围,防止误伤):

<?xml version="1.0" encoding="utf-8" ?>
<Weavers><MethodDecorator><IncludeAssemblies>MyCompany.MyApp.Application|MyCompany.MyApp.Domain</IncludeAssemblies><ExcludeAssemblies>Volo.Abp.*|Castle.*</ExcludeAssemblies></MethodDecorator>
</Weavers>

📊 编译期织入流程

开始编译
读取 FodyWeavers.xml 配置
筛选目标程序集
MethodDecorator.Fody 注入 IL 代码
生成已织入的 DLL
编译完成

4. 编译期切面 Attribute 🔍

日志 + 性能计时 + 异常捕获

using MethodDecorator.Fody.Interfaces;
using System;
using System.Diagnostics;
using System.Reflection;
using Serilog;[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class LogPerformanceAttribute : Attribute, IMethodDecorator
{private Stopwatch? _sw;private string _methodName = string.Empty;private readonly ILogger _log;private const int SlowThresholdMs = 200; // 可改为从配置读取public LogPerformanceAttribute(){_log = Log.ForContext<LogPerformanceAttribute>();}public void Init(object instance, MethodBase method, object[] args){_methodName = $"{method.DeclaringType?.Name}.{method.Name}";}public void OnEntry(){_sw = Stopwatch.StartNew();_log.Debug("🚀 [Enter] {Method}", _methodName);}public void OnExit(){if (_sw == null) return;_sw.Stop();var elapsed = _sw.ElapsedMilliseconds;if (elapsed >= SlowThresholdMs)_log.Warning("🐢 [Slow] {Method} took {Elapsed} ms", _methodName, elapsed);else_log.Information("✅ [Exit] {Method} in {Elapsed} ms", _methodName, elapsed);}public void OnException(Exception exception){_sw?.Stop();_log.Error(exception, "💥 [Error] {Method}", _methodName);}
}

注意async void 方法无法保证触发 OnExit(),不建议织入此类方法。


📊 方法调用运行流程

调用方ApplicationServiceLogPerformanceAttribute调用方法OnEntry()执行业务逻辑OnExit()OnException()alt[正常结束][异常]调用方ApplicationServiceLogPerformanceAttribute

5. 应用范围 🎯

  • 程序集级别

    [assembly: LogPerformance]
    
  • 类级别

    [LogPerformance]
    public class ProductAppService : ApplicationService { }
    
  • 方法级别

    public class OrderAppService : ApplicationService
    {[LogPerformance]public async Task<OrderDto> GetAsync(Guid id) { /* ... */ }
    }
    

💡 建议仅对 Application 层 织入,避免与 ABP 审计/日志拦截器重复打点。


6. Serilog 接入 📜

using Serilog;Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Async(a => a.Console()).Enrich.FromLogContext().Enrich.WithEnvironmentName().CreateLogger();try
{builder.Host.UseSerilog();
}
finally
{Log.CloseAndFlush();
}

初始化时机要在 HostBuilder 创建之前,避免被 ABP 默认日志覆盖。


7. 阈值配置化 🎚

public static class AopSettings
{public static int SlowThresholdMs { get; private set; } = 200;public static void Load(IConfiguration config)=> SlowThresholdMs = config.GetValue<int?>("AopMonitoring:SlowThresholdMs") ?? 200;
}
AopSettings.Load(builder.Configuration);

8. Benchmark 测试 🏎

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Order;[SimpleJob(BenchmarkDotNet.Jobs.RuntimeMoniker.Net80)]
[MemoryDiagnoser]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
[IterationCount(20)]
public class PerfTests
{private readonly ProductAppService _svc = new();[Benchmark(Baseline = true)]public Task NoAop_GetAsync() => _svc.GetAsync(Guid.NewGuid());[Benchmark]public Task FodyAop_GetAsync() => _svc.GetAsync(Guid.NewGuid());
}public static class Program
{public static void Main() => BenchmarkRunner.Run<PerfTests>();
}public class ProductAppService
{public virtual Task<int> GetAsync(Guid id) => Task.FromResult(42);
}

测试方法

  1. 关闭织入 → 编译 → 运行 Benchmark
  2. 开启织入 → 编译 → 运行 Benchmark
  3. 对比吞吐/耗时差异 📊

9. 常见问题与规避 ⚠

  • 冲突:用 IncludeAssemblies 限制范围,或关闭 ABP 某些拦截器。
  • 日志风暴:使用阈值日志、方法白名单。
  • 调试策略:Debug 全织入,Release 精准织入。
  • AOT/Trim 注意:NativeAOT 或裁剪发布时,需要保留反射元数据,否则 MethodBase 获取可能失效。

http://www.lryc.cn/news/614250.html

相关文章:

  • 当服务器多了时,如何管理?
  • 服务器快照与备份的本质区别及正确使用指南 (2025)
  • Linux 内核发包流程与路由控制实战
  • VMwareWorkstation17Pro安装CentOS8无法连接外网问题
  • python使用python-docx自动化操作word
  • Ideogram:优秀的在线AI绘画平台
  • 自由学习记录(79)
  • 3D TOF 视觉相机:工业视觉的破局者,重塑视觉感知的未来
  • 动态规划进阶:转移方程优化技巧全解
  • 二、RuoYi-Cloud-Plus 拉取到本地的准备和注意事项
  • AcWing 6478. 谁进线下了?III
  • C++使用FFmpeg进行视频推流
  • 【机器学习深度学习】微调训练数据质量
  • Android 之 ANR问题的全面解析与优化方案
  • CS231n2017 Lecture16 对抗样本与对抗训练笔记
  • Numpy科学计算与数据分析:Numpy布尔索引与花式索引实战
  • 如何板端编译OpenCV并搭建应用--基于瑞芯微米尔RK3576开发板
  • Spring系列之Spring AI入门
  • MySQL definer does not exist 问题分析
  • 一动鼠标就锁屏,设备活动监控方案的技术实现与应用
  • CPO-SVM分类预测+特征贡献SHAP分析,通过特征贡献分析增强模型透明度,Matlab代码实现,引入SHAP方法打破黑箱限制,提供全局及局部双重解释视角
  • ctrl+alt+方向键导致屏幕旋转的解决方法
  • Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2)
  • apiSQL网关调优:释放单节点的最大潜能
  • FreeRTOS---基础知识5
  • 【问题解决】使用patch-package修改node-models中的源码
  • Java 之 多态
  • CSS--后端也有自己的CSS要学
  • 腾讯 WeKnora 深度解析:大模型时代文档理解与检索的技术突破
  • Git 基础操作笔记(速查)