Skip to content

Commit 7fa55b6

Browse files
authored
feat: add crate attribute to trace macro (#105)
1 parent 5482429 commit 7fa55b6

2 files changed

Lines changed: 128 additions & 85 deletions

File tree

fastrace-macro/src/lib.rs

Lines changed: 110 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#[macro_use]
1010
extern crate proc_macro_error2;
1111

12-
use std::collections::HashMap;
12+
use std::collections::HashSet;
1313

1414
use proc_macro2::Span;
1515
use quote::quote_spanned;
@@ -19,85 +19,6 @@ use syn::punctuated::Punctuated;
1919
use syn::spanned::Spanned;
2020
use syn::*;
2121

22-
struct Args {
23-
name: Option<String>,
24-
short_name: bool,
25-
enter_on_poll: bool,
26-
properties: Vec<(String, String)>,
27-
}
28-
29-
struct Property {
30-
key: String,
31-
value: String,
32-
}
33-
34-
impl Parse for Property {
35-
fn parse(input: ParseStream) -> Result<Self> {
36-
let key: LitStr = input.parse()?;
37-
input.parse::<Token![:]>()?;
38-
let value: LitStr = input.parse()?;
39-
Ok(Property {
40-
key: key.value(),
41-
value: value.value(),
42-
})
43-
}
44-
}
45-
46-
impl Parse for Args {
47-
fn parse(input: ParseStream) -> Result<Self> {
48-
let mut name = None;
49-
let mut short_name = false;
50-
let mut enter_on_poll = false;
51-
let mut properties = Vec::new();
52-
let mut seen = HashMap::new();
53-
54-
while !input.is_empty() {
55-
let ident: Ident = input.parse()?;
56-
if seen.contains_key(&ident.to_string()) {
57-
return Err(Error::new(ident.span(), "duplicate argument"));
58-
}
59-
seen.insert(ident.to_string(), ());
60-
input.parse::<Token![=]>()?;
61-
match ident.to_string().as_str() {
62-
"name" => {
63-
let parsed_name: LitStr = input.parse()?;
64-
name = Some(parsed_name.value());
65-
}
66-
"short_name" => {
67-
let parsed_short_name: LitBool = input.parse()?;
68-
short_name = parsed_short_name.value;
69-
}
70-
"enter_on_poll" => {
71-
let parsed_enter_on_poll: LitBool = input.parse()?;
72-
enter_on_poll = parsed_enter_on_poll.value;
73-
}
74-
"properties" => {
75-
let content;
76-
let _brace_token = braced!(content in input);
77-
let property_list = content.parse_terminated(Property::parse, Token![,])?;
78-
for property in property_list {
79-
if properties.iter().any(|(k, _)| k == &property.key) {
80-
return Err(Error::new(Span::call_site(), "duplicate property key"));
81-
}
82-
properties.push((property.key, property.value));
83-
}
84-
}
85-
_ => return Err(Error::new(Span::call_site(), "unexpected identifier")),
86-
}
87-
if !input.is_empty() {
88-
let _ = input.parse::<Token![,]>();
89-
}
90-
}
91-
92-
Ok(Args {
93-
name,
94-
short_name,
95-
enter_on_poll,
96-
properties,
97-
})
98-
}
99-
}
100-
10122
/// An attribute macro designed to eliminate boilerplate code.
10223
///
10324
/// This macro automatically creates a span for the annotated function. The span name defaults to
@@ -117,6 +38,7 @@ impl Parse for Args {
11738
/// used. Only available for `async fn`. Defaults to `false`.
11839
/// * `properties` - A list of key-value pairs to be added as properties to the span. The value can
11940
/// be a format string, where the function arguments are accessible. Defaults to `{}`.
41+
/// * `crate` - The path to the fastrace crate. Defaults to `::fastrace`.
12042
///
12143
/// # Examples
12244
///
@@ -271,7 +193,109 @@ pub fn trace(
271193
.into()
272194
}
273195

196+
struct Args {
197+
name: Option<String>,
198+
short_name: bool,
199+
enter_on_poll: bool,
200+
properties: Vec<(String, String)>,
201+
crate_path: Path,
202+
}
203+
204+
impl Default for Args {
205+
fn default() -> Self {
206+
Self {
207+
name: None,
208+
short_name: false,
209+
enter_on_poll: false,
210+
properties: Vec::new(),
211+
crate_path: parse_quote!(::fastrace),
212+
}
213+
}
214+
}
215+
216+
struct Property {
217+
key: String,
218+
value: String,
219+
}
220+
221+
impl Parse for Property {
222+
fn parse(input: ParseStream) -> Result<Self> {
223+
let key: LitStr = input.parse()?;
224+
input.parse::<Token![:]>()?;
225+
let value: LitStr = input.parse()?;
226+
Ok(Property {
227+
key: key.value(),
228+
value: value.value(),
229+
})
230+
}
231+
}
232+
233+
impl Parse for Args {
234+
fn parse(input: ParseStream) -> Result<Self> {
235+
let mut name = None;
236+
let mut short_name = false;
237+
let mut enter_on_poll = false;
238+
let mut properties = Vec::new();
239+
let mut crate_path = parse_quote!(::fastrace);
240+
let mut seen = HashSet::new();
241+
242+
while !input.is_empty() {
243+
let key: Path = input.parse()?;
244+
let key = key
245+
.get_ident()
246+
.ok_or_else(|| Error::new(key.span(), "expected identifier"))?;
247+
if seen.contains(key) {
248+
return Err(Error::new(key.span(), "duplicate argument"));
249+
}
250+
seen.insert(key.clone());
251+
input.parse::<Token![=]>()?;
252+
match key.to_string().as_str() {
253+
"name" => {
254+
let parsed_name: LitStr = input.parse()?;
255+
name = Some(parsed_name.value());
256+
}
257+
"short_name" => {
258+
let parsed_short_name: LitBool = input.parse()?;
259+
short_name = parsed_short_name.value;
260+
}
261+
"enter_on_poll" => {
262+
let parsed_enter_on_poll: LitBool = input.parse()?;
263+
enter_on_poll = parsed_enter_on_poll.value;
264+
}
265+
"properties" => {
266+
let content;
267+
let _brace_token = braced!(content in input);
268+
let property_list = content.parse_terminated(Property::parse, Token![,])?;
269+
for property in property_list {
270+
if properties.iter().any(|(k, _)| k == &property.key) {
271+
return Err(Error::new(Span::call_site(), "duplicate property key"));
272+
}
273+
properties.push((property.key, property.value));
274+
}
275+
}
276+
"crate" => {
277+
let parsed_crate_path: Path = input.parse()?;
278+
crate_path = parsed_crate_path;
279+
}
280+
_ => return Err(Error::new(Span::call_site(), "unexpected identifier")),
281+
}
282+
if !input.is_empty() {
283+
let _ = input.parse::<Token![,]>();
284+
}
285+
}
286+
287+
Ok(Args {
288+
name,
289+
short_name,
290+
enter_on_poll,
291+
properties,
292+
crate_path,
293+
})
294+
}
295+
}
296+
274297
fn gen_name(span: Span, func_name: &str, args: &Args) -> proc_macro2::TokenStream {
298+
let crate_path = &args.crate_path;
275299
match &args.name {
276300
Some(name) if name.is_empty() => {
277301
abort_call_site!("`name` can not be empty")
@@ -291,7 +315,7 @@ fn gen_name(span: Span, func_name: &str, args: &Args) -> proc_macro2::TokenStrea
291315
}
292316
None => {
293317
quote_spanned!(span=>
294-
fastrace::func_path!()
318+
#crate_path::func_path!()
295319
)
296320
}
297321
}
@@ -350,23 +374,24 @@ fn gen_block(
350374
) -> proc_macro2::TokenStream {
351375
let name = gen_name(block.span(), func_name, args);
352376
let properties = gen_properties(block.span(), args);
377+
let crate_path = &args.crate_path;
353378

354379
// Generate the instrumented function body.
355380
// If the function is an `async fn`, this will wrap it in an async block.
356381
// Otherwise, this will enter the span and then perform the rest of the body.
357382
if async_context {
358383
let block = if args.enter_on_poll {
359384
quote_spanned!(block.span()=>
360-
fastrace::future::FutureExt::enter_on_poll(
385+
#crate_path::future::FutureExt::enter_on_poll(
361386
async move { #block },
362387
#name
363388
)
364389
)
365390
} else {
366391
quote_spanned!(block.span()=>
367392
{
368-
let __span__ = fastrace::Span::enter_with_local_parent( #name ) #properties;
369-
fastrace::future::FutureExt::in_span(
393+
let __span__ = #crate_path::Span::enter_with_local_parent( #name ) #properties;
394+
#crate_path::future::FutureExt::in_span(
370395
async move { #block },
371396
__span__,
372397
)
@@ -387,7 +412,7 @@ fn gen_block(
387412
}
388413

389414
quote_spanned!(block.span()=>
390-
let __guard__ = fastrace::local::LocalSpan::enter_with_local_parent( #name ) #properties;
415+
let __guard__ = #crate_path::local::LocalSpan::enter_with_local_parent( #name ) #properties;
391416
#block
392417
)
393418
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use fastrace as custom_fastrace;
2+
use custom_fastrace::trace;
3+
4+
#[trace(crate = custom_fastrace)]
5+
async fn f(a: u32) -> u32 {
6+
a
7+
}
8+
9+
#[trace(crate = ::fastrace, short_name = true)]
10+
fn sync_func() -> i32 {
11+
42
12+
}
13+
14+
#[tokio::main]
15+
async fn main() {
16+
f(1).await;
17+
sync_func();
18+
}

0 commit comments

Comments
 (0)