Hyz.Trace.Weaving.Fody
1.4.1
dotnet add package Hyz.Trace.Weaving.Fody --version 1.4.1
NuGet\Install-Package Hyz.Trace.Weaving.Fody -Version 1.4.1
<PackageReference Include="Hyz.Trace.Weaving.Fody" Version="1.4.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Hyz.Trace.Weaving.Fody" Version="1.4.1" />
<PackageReference Include="Hyz.Trace.Weaving.Fody"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Hyz.Trace.Weaving.Fody --version 1.4.1
#r "nuget: Hyz.Trace.Weaving.Fody, 1.4.1"
#:package Hyz.Trace.Weaving.Fody@1.4.1
#addin nuget:?package=Hyz.Trace.Weaving.Fody&version=1.4.1
#tool nuget:?package=Hyz.Trace.Weaving.Fody&version=1.4.1
Hyz.Trace.Weaving.Fody
Hyz.Trace 的 Fody IL 编织器 — 编译时扫描 [Trace] 特性,采用方法外壳包装(Thunk/Wrapper)模式自动织入 TraceScope 包装代码,实现方法级 Span 的零侵入捕获,理论编织成功率 100%。
特性优先级:类级
[Trace]编织类内所有方法(含 public/protected/internal/private/static);方法级[Trace]仅编织打标的单个方法。两者可混用,方法级标注总是生效。
安装
dotnet add package Hyz.Trace.Weaving.Fody
本包不包含 Hyz.Trace 核心库,需配合以下任一包使用:
Hyz.Trace.Client(客户端元包,推荐)Hyz.Trace(仅核心库)
安装后确保项目存在 FodyWeavers.xml(通常由 NuGet 包自动引入),其中启用本编织器:
<Weavers>
<Hyz.Trace.Weaving.Fody />
</Weavers>
[Trace] 特性详解
[Trace] 特性定义于 Hyz.Trace 命名空间,可标注于 类、方法、属性(AttributeTargets.Method | Class | Property,Inherited = true)。
特性属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Name |
string? |
null |
自定义 Span 名称。为空时使用 {TypeName}.{MethodName} |
IncludeParameters |
bool |
true |
是否将方法入参记录到 Span 的 inputParameters Tag |
IncludeReturnValue |
bool |
true |
是否将方法返回值记录到 Span 的 returnValue Tag |
标注位置与行为
| 标注位置 | 行为 |
|---|---|
| 标在类上 | 该类所有符合条件的方法均被编织(含 public/protected/internal/private/static,详见下方支持矩阵) |
| 标在方法上 | 仅该方法被编织;优先级高于类级标注,且不受类级标注影响 |
| 标在属性上 | 该属性的 get_/set_ 访问器被编织(Span 名为 {TypeName}.{PropName}.get / .set) |
混用规则:类级
[Trace]+ 方法级[Trace]可共存。方法级标注的方法一定会被编织(即使类未标注);类级标注会编织类内所有方法,方法级标注的方法同样会被编织,不会重复。
工作原理
方法外壳包装(Thunk/Wrapper)模式:与传统的内联 IL 修改方式不同,本编织器完全不修改原方法体,而是:
- 编译时 Fody 加载本 weaver DLL
- 扫描程序集中所有标注
[Trace]特性的方法或类 - 重命名原方法:将原方法重命名为
<MethodName>__TraceOriginal,改为private,原方法体 IL 保持 100% 不变 - 生成包装方法:创建一个与原方法签名完全一致的新方法(保留原访问级别与 static / virtual / override 修饰;显式接口
Overrides迁到包装方法,原方法改为 private non-virtual) - 织入追踪代码:包装方法内部通过
TraceScope.Run()/TraceScope.RunAsync()调用原方法,自动处理:- Span 创建与命名(默认
{TypeName}.{MethodName}) - 输入参数捕获
- 返回值捕获
- 异常自动捕获与
SetError() - 正常完成时
SetOk() using模式确保Dispose()始终执行
- Span 创建与命名(默认
- 对于
ref/out参数,采用直接 IL 路径避免闭包开销 - 内部调用自动重定向到包装方法,确保追踪连续性
核心优势:原方法体完全不被修改,从根本上避免了内联编织遇到复杂控制流(多 return、try-catch-finally、using、lock、迭代器、泛型方法、Dictionary 初始化器等)时的 IL 验证失败和 InvalidProgramException。
支持的方法类型
编织能力矩阵
| 方法类型 | 类级 [Trace] | 方法级 [Trace] | 说明 |
|---|---|---|---|
| 同步 void | ✅ | ✅ | SyncVoid 模式 |
| 同步有返回值(TResult) | ✅ | ✅ | SyncFunc 模式 |
| 异步 Task(无返回值) | ✅ | ✅ | AsyncTask 模式,async/await 原生支持 |
| 异步 Task<T> | ✅ | ✅ | AsyncTaskT 模式 |
| 异步 ValueTask / ValueTask<T> | ✅ | ✅ | 与 Task 同等处理 |
| 实例方法(public) | ✅ | ✅ | |
| 实例方法(protected/internal) | ✅ | ✅ | |
| 实例方法(private) | ✅ | ✅ | |
| 静态方法 | ✅ | ✅ | 包装方法自动加 static 修饰,不生成 <>4__this 字段 |
泛型方法(含 where T : class 等约束) |
✅ | ✅ | 自动处理类型泛型参数与方法泛型参数映射 |
ref / out / in 参数 |
✅ | ✅ | 直接 IL 路径,无闭包开销 |
| 属性 get/set([Trace] 标在属性上) | — | ✅ | Span 名 {Type}.{Prop}.get / .set |
| 复杂控制流(try-catch/using/lock/多 return/迭代器/递归/lambda 闭包) | ✅ | ✅ | 原方法体不修改,全部支持 |
不编织的方法(自动跳过)
| 方法类型 | 跳过原因 |
|---|---|
| 构造函数(实例/静态) | 不支持编织 |
abstract 方法 |
无方法体 |
runtime/internal call/unmanaged 方法 |
无 IL 方法体 |
| 无方法体方法(如 P/Invoke、外部方法) | 无法包装 |
| 编译器生成方法(get_/set_/move_next 等) | 类级 [Trace] 自动跳过;但属性/事件显式标 [Trace] 时其访问器会被编织 |
ref/out 参数 + async Task 返回类型组合 |
组合无法安全编织,跳过并告警 |
使用示例
方法级追踪
using Hyz.Trace;
public class UserService
{
// 自动生成 Span,名称为 "UserService.GetUser"
[Trace]
public User? GetUser(int id)
{
return userRepository.Find(id);
}
}
类级追踪(覆盖所有方法)
using Hyz.Trace;
[Trace]
public class OrderService
{
public Order CreateOrder(CreateOrderRequest request) { /* 会被编织 */ }
public void CancelOrder(int orderId) { /* 会被编织 */ }
// 类级 [Trace] 同样编织 private / protected / static 方法
private void InternalHelper() { /* 会被编织 */ }
protected virtual void Validate(Order order) { /* 会被编织 */ }
public static Order? FindById(int id) { /* 会被编织 */ }
// 构造函数不会被编织(不支持)
public OrderService() { }
}
自定义 Span 名称与参数控制
public class PaymentService
{
[Trace(Name = "process-payment")]
public PaymentResult Process(PaymentRequest request) { /* ... */ }
// 关闭入参/返回值记录(避免敏感数据或大对象进入 Span)
[Trace(IncludeParameters = false, IncludeReturnValue = false)]
public string GetToken() { /* ... */ }
}
属性级追踪
public class ConfigProvider
{
// Span 名为 "ConfigProvider.Cache.get" / ".set"
[Trace]
public string? Cache { get; set; }
}
复杂方法也能正常编织
[Trace]
public static HttpResult<T> WebRequest<T>(string url, Dictionary<string, string>? headers = null)
{
// 任意复杂控制流都不会导致编织失败:
// 多 return、try-catch-finally、using、lock、迭代器、递归、lambda 闭包、泛型约束
// 原方法体完全保持不变
}
编译日志
编织过程会在 Visual Studio 生成输出中打印详细日志(前缀 [Hyz.Trace.Weaving]):
Normal 详细级别:
OK {Kind} {MethodName}:编织成功(显示方法类型:SyncVoid/SyncFunc/AsyncTask/AsyncTaskT)SKIP {Source} {MethodName} : {原因}:跳过编织(Source为[Trace]或[类级Trace])- 汇总统计:总计扫描/成功/跳过/失败数量
Detailed 详细级别:
FAIL {MethodName} : {错误信息}:编织失败(带完整异常堆栈)- 失败状态始终在 Normal 级别显示为警告
日志示例
[Hyz.Trace.Weaving] =========================================================
[Hyz.Trace.Weaving] 开始扫描程序集: Hyz.Trace.Demo.NetFramework.Console
[Hyz.Trace.Weaving] =========================================================
[Hyz.Trace.Weaving] --- Hyz.Trace.Demo.NetFramework.TraceConsole.Services.OrderService ---
[Hyz.Trace.Weaving] OK SyncFunc CreateOrder
[Hyz.Trace.Weaving] OK SyncFunc GetOrder
[Hyz.Trace.Weaving] --- Hyz.Trace.Demo.NetFramework.TraceConsole.Services.SafetyStressService ---
[Hyz.Trace.Weaving] OK SyncFunc PrivateGeneric
[Hyz.Trace.Weaving] OK SyncFunc PublicWithUsingAndThrow
[Hyz.Trace.Weaving] OK SyncFunc MultiBranchServiceCallHandle
[Hyz.Trace.Weaving] ...
[Hyz.Trace.Weaving] =========================================================
[Hyz.Trace.Weaving] 编织完成: 总计 86, 成功 49, 跳过 37, 失败 0
[Hyz.Trace.Weaving] =========================================================
安全保障机制
- 原方法体零修改:从根本上避免 IL 损坏
- 原子提交:新类型/方法先添加到模块树,再生成 IL,失败时自动回滚
- 运行时保护:
TraceScope公共方法均有 try-catch,追踪异常绝不影响业务代码 - IL 栈验证:编织完成后验证方法体 IL 栈深度正确性
与 SourceGenerator 对比
| 维度 | Fody IL 编织 | SourceGenerator 源生成器 |
|---|---|---|
| 实现方式 | 编译后修改 IL(方法外壳包装) | 编译时生成 Wrapper 装饰器类 |
| 类级 [Trace] 覆盖范围 | 所有方法(含 private/protected/static) | 仅 public/internal 实例方法 |
| 方法级 [Trace] 覆盖范围 | 所有访问级别(含 private/protected/static) | 仅 public/internal 实例方法 |
| 属性级 [Trace] | ✅ | ✅(public/internal 属性) |
| 静态方法 | ✅ | ❌(装饰器模式无法包装静态方法) |
| private/protected 方法 | ✅ | ❌(Wrapper 无法访问) |
| 使用方式 | 零侵入,标记即生效 | 需通过 TraceFactory.Wrap() / AddTracedServices() 显式包装 |
| DI 场景 | 自动(透明) | 需注册 Wrapper |
| 目标框架 | .NET Standard 2.0(.NET Framework 4.6.2+ / .NET Core 2.0+ / .NET 5+) | .NET 8.0+(Roslyn 增量生成器) |
| 与对方共存 | 智能共存(SuppressDirectWeaving 防双重追踪) |
智能共存 |
选型建议:优先使用 Fody(功能完整、零侵入、全平台)。SourceGenerator 作为补充,仅用于无法安装 Fody 或必须使用源生成器的场景。
与元包的关系
Hyz.Trace.Client 元包默认不包含本包,以避免与独立引用时的双重编织冲突。如需 IL 编织能力,请显式安装本包。
依赖
- Fody 6.9.3(自动作为传递依赖引入)
Hyz.Trace核心库(需使用者自行引用)
目标框架
netstandard2.0 — 兼容所有支持 .NET Standard 2.0 的运行时(.NET Framework 4.6.2+、.NET Core 2.0+、.NET 5+)。
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Fody (>= 6.9.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.4.1 | 105 | 8/12/2026 |
| 1.4.0 | 92 | 8/11/2026 |
| 1.3.9 | 87 | 8/11/2026 |
| 1.3.8 | 99 | 8/10/2026 |
| 1.3.7 | 93 | 8/7/2026 |
| 1.3.6 | 94 | 8/7/2026 |
| 1.3.5 | 93 | 8/7/2026 |
| 1.3.4 | 93 | 8/7/2026 |
| 1.3.3 | 89 | 8/7/2026 |
| 1.3.2 | 97 | 8/6/2026 |
| 1.3.1 | 101 | 8/6/2026 |
| 1.3.0 | 97 | 8/4/2026 |
| 1.2.7 | 105 | 8/3/2026 |
| 1.2.6 | 102 | 8/2/2026 |
| 1.2.5 | 101 | 8/2/2026 |
| 1.2.4 | 107 | 8/2/2026 |
| 1.2.3 | 100 | 8/2/2026 |
| 1.2.2 | 100 | 8/1/2026 |
| 1.2.1 | 105 | 8/1/2026 |
| 1.2.0 | 101 | 8/1/2026 |