mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-10-02 23:34:40 +00:00

This pull request adds support to the `#[instrument]` macro for destructured arguments. ## Motivation The `#[instrument]` macro automatically adds function arguments as fields on the `Span`. Previously, it would only do so for "plain" `Ident` argument types, but would ignore destructured arguments. ## Solution By recursively pattern matching each argument to find all identifiers it creates, we can construct an authoritative list of all input names. * attributes: extract param_names extractor To enable the recursion needed to extract all the various destructured parameter types. * attributes: support destructured tuple arguments * attributes: support destructured ref arguments * attributes: support destructured tuple struct arguments * attributes: support destructured struct arguments * attributes: add crazy destructuring test This is to test the combination of all the supported function argument destructuring types. * attributes: move destructuring tests to their own file * attributes: apply cargo fmt * attributes: add helpful comment to pattern pattern match Since function signatures can only use irrefutable patterns, we can be confident that no legal code will have any other variant of `syn::Pat` than the ones we match on. However, to prevent anyone else from going down the rabbit hole of wondering about it, we add a helpful comment. * attributes: support self argument as span field
203 lines
4.9 KiB
Rust
203 lines
4.9 KiB
Rust
mod support;
|
|
use support::*;
|
|
|
|
use tracing::subscriber::with_default;
|
|
use tracing::Level;
|
|
use tracing_attributes::instrument;
|
|
|
|
#[test]
|
|
fn override_everything() {
|
|
#[instrument(target = "my_target", level = "debug")]
|
|
fn my_fn() {}
|
|
|
|
#[instrument(level = "debug", target = "my_target")]
|
|
fn my_other_fn() {}
|
|
|
|
let span = span::mock()
|
|
.named("my_fn")
|
|
.at_level(Level::DEBUG)
|
|
.with_target("my_target");
|
|
let span2 = span::mock()
|
|
.named("my_other_fn")
|
|
.at_level(Level::DEBUG)
|
|
.with_target("my_target");
|
|
let (subscriber, handle) = subscriber::mock()
|
|
.new_span(span.clone())
|
|
.enter(span.clone())
|
|
.exit(span.clone())
|
|
.drop_span(span)
|
|
.new_span(span2.clone())
|
|
.enter(span2.clone())
|
|
.exit(span2.clone())
|
|
.drop_span(span2)
|
|
.done()
|
|
.run_with_handle();
|
|
|
|
with_default(subscriber, || {
|
|
my_fn();
|
|
my_other_fn();
|
|
});
|
|
|
|
handle.assert_finished();
|
|
}
|
|
|
|
#[test]
|
|
fn fields() {
|
|
#[instrument(target = "my_target", level = "debug")]
|
|
fn my_fn(arg1: usize, arg2: bool) {}
|
|
|
|
let span = span::mock()
|
|
.named("my_fn")
|
|
.at_level(Level::DEBUG)
|
|
.with_target("my_target");
|
|
|
|
let span2 = span::mock()
|
|
.named("my_fn")
|
|
.at_level(Level::DEBUG)
|
|
.with_target("my_target");
|
|
let (subscriber, handle) = subscriber::mock()
|
|
.new_span(
|
|
span.clone().with_field(
|
|
field::mock("arg1")
|
|
.with_value(&format_args!("2"))
|
|
.and(field::mock("arg2").with_value(&format_args!("false")))
|
|
.only(),
|
|
),
|
|
)
|
|
.enter(span.clone())
|
|
.exit(span.clone())
|
|
.drop_span(span)
|
|
.new_span(
|
|
span2.clone().with_field(
|
|
field::mock("arg1")
|
|
.with_value(&format_args!("3"))
|
|
.and(field::mock("arg2").with_value(&format_args!("true")))
|
|
.only(),
|
|
),
|
|
)
|
|
.enter(span2.clone())
|
|
.exit(span2.clone())
|
|
.drop_span(span2)
|
|
.done()
|
|
.run_with_handle();
|
|
|
|
with_default(subscriber, || {
|
|
my_fn(2, false);
|
|
my_fn(3, true);
|
|
});
|
|
|
|
handle.assert_finished();
|
|
}
|
|
|
|
#[test]
|
|
fn skip() {
|
|
struct UnDebug(pub u32);
|
|
|
|
#[instrument(target = "my_target", level = "debug", skip(_arg2, _arg3))]
|
|
fn my_fn(arg1: usize, _arg2: UnDebug, _arg3: UnDebug) {}
|
|
|
|
let span = span::mock()
|
|
.named("my_fn")
|
|
.at_level(Level::DEBUG)
|
|
.with_target("my_target");
|
|
|
|
let span2 = span::mock()
|
|
.named("my_fn")
|
|
.at_level(Level::DEBUG)
|
|
.with_target("my_target");
|
|
let (subscriber, handle) = subscriber::mock()
|
|
.new_span(
|
|
span.clone()
|
|
.with_field(field::mock("arg1").with_value(&format_args!("2")).only()),
|
|
)
|
|
.enter(span.clone())
|
|
.exit(span.clone())
|
|
.drop_span(span)
|
|
.new_span(
|
|
span2
|
|
.clone()
|
|
.with_field(field::mock("arg1").with_value(&format_args!("3")).only()),
|
|
)
|
|
.enter(span2.clone())
|
|
.exit(span2.clone())
|
|
.drop_span(span2)
|
|
.done()
|
|
.run_with_handle();
|
|
|
|
with_default(subscriber, || {
|
|
my_fn(2, UnDebug(0), UnDebug(1));
|
|
my_fn(3, UnDebug(0), UnDebug(1));
|
|
});
|
|
|
|
handle.assert_finished();
|
|
}
|
|
|
|
#[test]
|
|
fn generics() {
|
|
#[derive(Debug)]
|
|
struct Foo;
|
|
|
|
#[instrument]
|
|
fn my_fn<S, T: std::fmt::Debug>(arg1: S, arg2: T)
|
|
where
|
|
S: std::fmt::Debug,
|
|
{
|
|
}
|
|
|
|
let span = span::mock().named("my_fn");
|
|
|
|
let (subscriber, handle) = subscriber::mock()
|
|
.new_span(
|
|
span.clone().with_field(
|
|
field::mock("arg1")
|
|
.with_value(&format_args!("Foo"))
|
|
.and(field::mock("arg2").with_value(&format_args!("false"))),
|
|
),
|
|
)
|
|
.enter(span.clone())
|
|
.exit(span.clone())
|
|
.drop_span(span)
|
|
.done()
|
|
.run_with_handle();
|
|
|
|
with_default(subscriber, || {
|
|
my_fn(Foo, false);
|
|
});
|
|
|
|
handle.assert_finished();
|
|
}
|
|
|
|
#[test]
|
|
fn methods() {
|
|
#[derive(Debug)]
|
|
struct Foo;
|
|
|
|
impl Foo {
|
|
#[instrument]
|
|
fn my_fn(&self, arg1: usize) {}
|
|
}
|
|
|
|
let span = span::mock().named("my_fn");
|
|
|
|
let (subscriber, handle) = subscriber::mock()
|
|
.new_span(
|
|
span.clone().with_field(
|
|
field::mock("self")
|
|
.with_value(&format_args!("Foo"))
|
|
.and(field::mock("arg1").with_value(&format_args!("42"))),
|
|
),
|
|
)
|
|
.enter(span.clone())
|
|
.exit(span.clone())
|
|
.drop_span(span)
|
|
.done()
|
|
.run_with_handle();
|
|
|
|
with_default(subscriber, || {
|
|
let foo = Foo;
|
|
foo.my_fn(42);
|
|
});
|
|
|
|
handle.assert_finished();
|
|
}
|