如何为Rust crate中所有函数自动添加#[tracing::instrument]属性?
问题:如何给Rust crate中所有函数批量添加
#[tracing::instrument]属性? 我想给我的Rust crate里的所有函数定义都加上#[tracing::instrument]属性,这能实现吗?我自己写了一个作用于根模块的proc macro,用syn遍历整个crate里的所有fn并注入该属性,但这个方案兼容性很差,碰到了两个错误:
第一个错误:
error[E0658]: inner macro attributes are unstable ... = note: see issue #54726 for more information = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable
第二个错误:
error[E0658]: non-inline modules in proc macro input are unstable ... = note: see issue #54727 for more information = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
有没有更靠谱的实现方式?
解决方案
直接写根模块级的proc macro会依赖Rust的不稳定特性,没法在稳定版环境下正常工作,推荐以下几种更实用的方案:
1. 用现成的批量instrument第三方crate
可以使用tracing-instrument-all这类专门做批量instrument的crate,它基于稳定版Rust实现,不需要依赖任何不稳定特性。只需在crate根文件的顶部添加一行标注,就能自动给整个模块(包括所有子模块)下的所有函数加上#[tracing::instrument]:
#[tracing_instrument_all::instrument_all] mod crate_root { // 你的所有模块、函数代码都写在这里面 }
2. 用rustfmt自定义规则半自动批量修改
如果不想引入额外依赖,可以通过rustfmt的自定义规则脚本批量修改源码:
- 编写匹配fn定义的rustfmt自定义规则,自动在每个fn上方插入
#[tracing::instrument]属性 - 运行
cargo fmt触发批量修改
这种方式是一次性修改源码,适合不需要动态添加属性的场景,没有运行时依赖。
3. 模块级批量标注(可控性强)
如果想自己实现且依赖稳定特性,可以写一个仅处理当前模块内联子模块的proc macro,避免触发非内联模块的不稳定错误。示例代码如下:
use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Item, ItemMod}; #[proc_macro_attribute] pub fn instrument_all(_attrs: TokenStream, input: TokenStream) -> TokenStream { let mut module = parse_macro_input!(input as ItemMod); if let Some((_, items)) = &mut module.content { instrument_items(items); } quote!(#module).into() } fn instrument_items(items: &mut Vec<Item>) { for item in items { match item { Item::Fn(func) => { func.attrs.push(syn::parse_quote!(#[tracing::instrument])); } Item::Mod(submod) => { if let Some((_, sub_items)) = &mut submod.content { instrument_items(sub_items); } } _ => {} } } }
之后你可以在每个需要批量标注的模块顶部添加#[instrument_all],逐步覆盖整个crate,这种方式更可控,且完全基于稳定版Rust特性。
内容的提问来源于stack exchange,提问作者ajp
相关产品推荐
相关产品推荐

