Skip to content

Commit 37d55b6

Browse files
committed
attributes: don't record primitive types of the function arguments as fmt::Debug (tokio-rs#1378)
The default behavior of `tracing::instrument` attribution will record all of the function arguments as `fmt::Debug`, which is overwhelmed and unnecessary for those primitive types, such as `bool`, `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `usize`, and `isize`. Another concerning reason is that we‘ll lose the type info of those primitive types when record by a `Visitor`, while those type infos is essential to some people. For example, I need to show my spans in Jaeger UI. Make the `tracing::instrument` records other function arguments as `fmt::Debug ` while not for those primitive types. However, I'm not good at naming. Maybe the `RecordType` enum and its variant aren't a good name? I'd love to seek suggestions. Thanks.
1 parent bc5a4e6 commit 37d55b6

File tree

6 files changed

+118
-32
lines changed

6 files changed

+118
-32
lines changed

tracing-attributes/src/lib.rs

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,15 @@ use syn::{
9898
/// Instruments a function to create and enter a `tracing` [span] every time
9999
/// the function is called.
100100
///
101-
/// By default, the generated span's [name] will be the name of the function,
102-
/// the span's [target] will be the current module path, and the span's [level]
103-
/// will be [`INFO`], although these properties can be overridden. Any arguments
104-
/// to that function will be recorded as fields using [`fmt::Debug`].
101+
/// Unless overriden, a span with `info` level will be generated.
102+
/// The generated span's name will be the name of the function.
103+
/// By default, all arguments to the function are included as fields on the
104+
/// span. Arguments that are `tracing` [primitive types] implementing the
105+
/// [`Value` trait] will be recorded as fields of that type. Types which do
106+
/// not implement `Value` will be recorded using [`std::fmt::Debug`].
107+
///
108+
/// [primitive types]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html#foreign-impls
109+
/// [`Value` trait]: https://docs.rs/tracing/latest/tracing/field/trait.Value.html.
105110
///
106111
/// # Overriding Span Attributes
107112
///
@@ -591,12 +596,17 @@ fn gen_block(
591596
let span = (|| {
592597
// Pull out the arguments-to-be-skipped first, so we can filter results
593598
// below.
594-
let param_names: Vec<(Ident, Ident)> = params
599+
let param_names: Vec<(Ident, (Ident, RecordType))> = params
595600
.clone()
596601
.into_iter()
597602
.flat_map(|param| match param {
598-
FnArg::Typed(PatType { pat, .. }) => param_names(*pat),
599-
FnArg::Receiver(_) => Box::new(iter::once(Ident::new("self", param.span()))),
603+
FnArg::Typed(PatType { pat, ty, .. }) => {
604+
param_names(*pat, RecordType::parse_from_ty(&*ty))
605+
}
606+
FnArg::Receiver(_) => Box::new(iter::once((
607+
Ident::new("self", param.span()),
608+
RecordType::Debug,
609+
))),
600610
})
601611
// Little dance with new (user-exposed) names and old (internal)
602612
// names of identifiers. That way, we could do the following
@@ -608,13 +618,13 @@ fn gen_block(
608618
// async fn foo(&self, v: usize) {}
609619
// }
610620
// ```
611-
.map(|x| {
621+
.map(|(x, record_type)| {
612622
// if we are inside a function generated by async-trait <=0.1.43, we need to
613623
// take care to rewrite "_self" as "self" for 'user convenience'
614624
if self_type.is_some() && x == "_self" {
615-
(Ident::new("self", x.span()), x)
625+
(Ident::new("self", x.span()), (x, record_type))
616626
} else {
617-
(x.clone(), x)
627+
(x.clone(), (x, record_type))
618628
}
619629
})
620630
.collect();
@@ -649,13 +659,16 @@ fn gen_block(
649659
true
650660
}
651661
})
652-
.map(|(user_name, real_name)| quote!(#user_name = tracing::field::debug(&#real_name)))
662+
.map(|(user_name, (real_name, record_type))| match record_type {
663+
RecordType::Value => quote!(#user_name = #real_name),
664+
RecordType::Debug => quote!(#user_name = tracing::field::debug(&#real_name)),
665+
})
653666
.collect();
654667

655668
// replace every use of a variable with its original name
656669
if let Some(Fields(ref mut fields)) = args.fields {
657670
let mut replacer = IdentAndTypesRenamer {
658-
idents: param_names,
671+
idents: param_names.into_iter().map(|(a, (b, _))| (a, b)).collect(),
659672
types: Vec::new(),
660673
};
661674

@@ -1044,20 +1057,93 @@ impl Parse for Level {
10441057
}
10451058
}
10461059

1047-
fn param_names(pat: Pat) -> Box<dyn Iterator<Item = Ident>> {
1060+
/// Indicates whether a field should be recorded as `Value` or `Debug`.
1061+
enum RecordType {
1062+
/// The field should be recorded using its `Value` implementation.
1063+
Value,
1064+
/// The field should be recorded using `tracing::field::debug()`.
1065+
Debug,
1066+
}
1067+
1068+
impl RecordType {
1069+
/// Array of primitive types which should be recorded as [RecordType::Value].
1070+
const TYPES_FOR_VALUE: [&'static str; 23] = [
1071+
"bool",
1072+
"str",
1073+
"u8",
1074+
"i8",
1075+
"u16",
1076+
"i16",
1077+
"u32",
1078+
"i32",
1079+
"u64",
1080+
"i64",
1081+
"usize",
1082+
"isize",
1083+
"NonZeroU8",
1084+
"NonZeroI8",
1085+
"NonZeroU16",
1086+
"NonZeroI16",
1087+
"NonZeroU32",
1088+
"NonZeroI32",
1089+
"NonZeroU64",
1090+
"NonZeroI64",
1091+
"NonZeroUsize",
1092+
"NonZeroIsize",
1093+
"Wrapping",
1094+
];
1095+
1096+
/// Parse `RecordType` from [syn::Type] by looking up
1097+
/// the [RecordType::TYPES_FOR_VALUE] array.
1098+
fn parse_from_ty(ty: &syn::Type) -> Self {
1099+
match ty {
1100+
syn::Type::Path(syn::TypePath { path, .. })
1101+
if path
1102+
.segments
1103+
.iter()
1104+
.last()
1105+
.map(|path_segment| {
1106+
let ident = path_segment.ident.to_string();
1107+
Self::TYPES_FOR_VALUE.iter().any(|&t| t == ident)
1108+
})
1109+
.unwrap_or(false) =>
1110+
{
1111+
RecordType::Value
1112+
}
1113+
syn::Type::Reference(syn::TypeReference { elem, .. }) => {
1114+
RecordType::parse_from_ty(&*elem)
1115+
}
1116+
_ => RecordType::Debug,
1117+
}
1118+
}
1119+
}
1120+
1121+
fn param_names(pat: Pat, record_type: RecordType) -> Box<dyn Iterator<Item = (Ident, RecordType)>> {
10481122
match pat {
1049-
Pat::Ident(PatIdent { ident, .. }) => Box::new(iter::once(ident)),
1050-
Pat::Reference(PatReference { pat, .. }) => param_names(*pat),
1123+
Pat::Ident(PatIdent { ident, .. }) => Box::new(iter::once((ident, record_type))),
1124+
Pat::Reference(PatReference { pat, .. }) => param_names(*pat, record_type),
1125+
// We can't get the concrete type of fields in the struct/tuple
1126+
// patterns by using `syn`. e.g. `fn foo(Foo { x, y }: Foo) {}`.
1127+
// Therefore, the struct/tuple patterns in the arguments will just
1128+
// always be recorded as `RecordType::Debug`.
10511129
Pat::Struct(PatStruct { fields, .. }) => Box::new(
10521130
fields
10531131
.into_iter()
1054-
.flat_map(|FieldPat { pat, .. }| param_names(*pat)),
1132+
.flat_map(|FieldPat { pat, .. }| param_names(*pat, RecordType::Debug)),
1133+
),
1134+
Pat::Tuple(PatTuple { elems, .. }) => Box::new(
1135+
elems
1136+
.into_iter()
1137+
.flat_map(|p| param_names(p, RecordType::Debug)),
10551138
),
1056-
Pat::Tuple(PatTuple { elems, .. }) => Box::new(elems.into_iter().flat_map(param_names)),
10571139
Pat::TupleStruct(PatTupleStruct {
10581140
pat: PatTuple { elems, .. },
10591141
..
1060-
}) => Box::new(elems.into_iter().flat_map(param_names)),
1142+
}) => Box::new(
1143+
elems
1144+
.into_iter()
1145+
.flat_map(|p| param_names(p, RecordType::Debug)),
1146+
),
10611147

10621148
// The above *should* cover all cases of irrefutable patterns,
10631149
// but we purposefully don't do any funny business here

tracing-attributes/tests/async_fn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ fn async_fn_with_async_trait_and_fields_expressions() {
182182
.new_span(
183183
span.clone().with_field(
184184
field::mock("_v")
185-
.with_value(&tracing::field::debug(5))
185+
.with_value(&5usize)
186186
.and(field::mock("test").with_value(&tracing::field::debug(10)))
187187
.and(field::mock("val").with_value(&42u64))
188188
.and(field::mock("val2").with_value(&42u64)),

tracing-attributes/tests/destructuring.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ fn destructure_refs() {
7474
let (subscriber, handle) = subscriber::mock()
7575
.new_span(
7676
span.clone()
77-
.with_field(field::mock("arg1").with_value(&format_args!("1")).only()),
77+
.with_field(field::mock("arg1").with_value(&1usize).only()),
7878
)
7979
.enter(span.clone())
8080
.exit(span.clone())

tracing-attributes/tests/err.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ fn impl_trait_return_type() {
131131
let (subscriber, handle) = subscriber::mock()
132132
.new_span(
133133
span.clone()
134-
.with_field(field::mock("x").with_value(&format_args!("10")).only()),
134+
.with_field(field::mock("x").with_value(&10usize).only()),
135135
)
136136
.enter(span.clone())
137137
.exit(span.clone())

tracing-attributes/tests/fields.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ fn fields() {
6161
fn expr_field() {
6262
let span = span::mock().with_field(
6363
mock("s")
64-
.with_value(&tracing::field::debug("hello world"))
64+
.with_value(&"hello world")
6565
.and(mock("len").with_value(&"hello world".len()))
6666
.only(),
6767
);
@@ -74,7 +74,7 @@ fn expr_field() {
7474
fn two_expr_fields() {
7575
let span = span::mock().with_field(
7676
mock("s")
77-
.with_value(&tracing::field::debug("hello world"))
77+
.with_value(&"hello world")
7878
.and(mock("s.len").with_value(&"hello world".len()))
7979
.and(mock("s.is_empty").with_value(&false))
8080
.only(),
@@ -120,7 +120,7 @@ fn parameters_with_fields() {
120120
let span = span::mock().with_field(
121121
mock("foo")
122122
.with_value(&"bar")
123-
.and(mock("param").with_value(&format_args!("1")))
123+
.and(mock("param").with_value(&1u32))
124124
.only(),
125125
);
126126
run_test(span, || {

tracing-attributes/tests/instrument.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ fn fields() {
5959
.new_span(
6060
span.clone().with_field(
6161
field::mock("arg1")
62-
.with_value(&format_args!("2"))
63-
.and(field::mock("arg2").with_value(&format_args!("false")))
62+
.with_value(&2usize)
63+
.and(field::mock("arg2").with_value(&false))
6464
.only(),
6565
),
6666
)
@@ -70,8 +70,8 @@ fn fields() {
7070
.new_span(
7171
span2.clone().with_field(
7272
field::mock("arg1")
73-
.with_value(&format_args!("3"))
74-
.and(field::mock("arg2").with_value(&format_args!("true")))
73+
.with_value(&3usize)
74+
.and(field::mock("arg2").with_value(&true))
7575
.only(),
7676
),
7777
)
@@ -108,15 +108,15 @@ fn skip() {
108108
let (subscriber, handle) = subscriber::mock()
109109
.new_span(
110110
span.clone()
111-
.with_field(field::mock("arg1").with_value(&format_args!("2")).only()),
111+
.with_field(field::mock("arg1").with_value(&2usize).only()),
112112
)
113113
.enter(span.clone())
114114
.exit(span.clone())
115115
.drop_span(span)
116116
.new_span(
117117
span2
118118
.clone()
119-
.with_field(field::mock("arg1").with_value(&format_args!("3")).only()),
119+
.with_field(field::mock("arg1").with_value(&3usize).only()),
120120
)
121121
.enter(span2.clone())
122122
.exit(span2.clone())
@@ -184,7 +184,7 @@ fn methods() {
184184
span.clone().with_field(
185185
field::mock("self")
186186
.with_value(&format_args!("Foo"))
187-
.and(field::mock("arg1").with_value(&format_args!("42"))),
187+
.and(field::mock("arg1").with_value(&42usize)),
188188
),
189189
)
190190
.enter(span.clone())
@@ -213,7 +213,7 @@ fn impl_trait_return_type() {
213213
let (subscriber, handle) = subscriber::mock()
214214
.new_span(
215215
span.clone()
216-
.with_field(field::mock("x").with_value(&format_args!("10")).only()),
216+
.with_field(field::mock("x").with_value(&10usize).only()),
217217
)
218218
.enter(span.clone())
219219
.exit(span.clone())

0 commit comments

Comments
 (0)