Attribute Macro bevy::utils::tracing::instrument
source · #[instrument]
Expand description
Instruments a function to create and enter a tracing
span every time
the function is called.
Unless overridden, a span with the INFO
level will be generated.
The generated span’s name will be the name of the function.
By default, all arguments to the function are included as fields on the
span. Arguments that are tracing
primitive types implementing the
Value
trait will be recorded as fields of that type. Types which do
not implement Value
will be recorded using [std::fmt::Debug
].
Overriding Span Attributes
To change the name of the generated span, add a name
argument to the
#[instrument]
macro, followed by an equals sign and a string literal. For
example:
// The generated span's name will be "my_span" rather than "my_function".
#[instrument(name = "my_span")]
pub fn my_function() {
// ... do something incredibly interesting and important ...
}
To override the target of the generated span, add a target
argument to
the #[instrument]
macro, followed by an equals sign and a string literal
for the new target. The module path is still recorded separately. For
example:
pub mod my_module {
// The generated span's target will be "my_crate::some_special_target",
// rather than "my_crate::my_module".
#[instrument(target = "my_crate::some_special_target")]
pub fn my_function() {
// ... all kinds of neat code in here ...
}
}
Finally, to override the level of the generated span, add a level
argument, followed by an equals sign and a string literal with the name of
the desired level. Level names are not case sensitive. For example:
// The span's level will be TRACE rather than INFO.
#[instrument(level = "trace")]
pub fn my_function() {
// ... I have written a truly marvelous implementation of this function,
// which this example is too narrow to contain ...
}
Skipping Fields
To skip recording one or more arguments to a function or method, pass
the argument’s name inside the skip()
argument on the #[instrument]
macro. This can be used when an argument to an instrumented function does
not implement fmt::Debug
, or to exclude an argument with a verbose or
costly Debug
implementation. Note that:
- multiple argument names can be passed to
skip
. - arguments passed to
skip
do not need to implementfmt::Debug
.
You can also use skip_all
to skip all arguments.
Examples
// This type doesn't implement `fmt::Debug`!
struct NonDebug;
// `arg` will be recorded, while `non_debug` will not.
#[instrument(skip(non_debug))]
fn my_function(arg: usize, non_debug: NonDebug) {
// ...
}
// These arguments are huge
#[instrument(skip_all)]
fn my_big_data_function(large: Vec<u8>, also_large: HashMap<String, String>) {
// ...
}
Skipping the self
parameter:
#[derive(Debug)]
struct MyType {
data: Vec<u8>, // Suppose this buffer is often quite long...
}
impl MyType {
// Suppose we don't want to print an entire kilobyte of `data`
// every time this is called...
#[instrument(skip(self))]
pub fn my_method(&mut self, an_interesting_argument: usize) {
// ... do something (hopefully, using all that `data`!)
}
}
Adding Fields
Additional fields (key-value pairs with arbitrary data) can be passed to
to the generated span through the fields
argument on the
#[instrument]
macro. Strings, integers or boolean literals are accepted values
for each field. The name of the field must be a single valid Rust
identifier, nested (dotted) field names are not supported. Any
Rust expression can be used as a field value in this manner. These
expressions will be evaluated at the beginning of the function’s body, so
arguments to the function may be used in these expressions. Field names may
also be specified without values. Doing so will result in an empty field
whose value may be recorded later within the function body.
Note that overlap between the names of fields and (non-skipped) arguments will result in a compile error.
Examples
Adding a new field based on the value of an argument:
// This will record a field named "i" with the value of `i` *and* a field
// named "next" with the value of `i` + 1.
#[instrument(fields(next = i + 1))]
pub fn my_function(i: usize) {
// ...
}
Recording specific properties of a struct as their own fields:
// This will record the request's URI and HTTP method as their own separate
// fields.
#[instrument(fields(http.uri = req.uri(), http.method = req.method()))]
pub fn handle_request<B>(req: http::Request<B>) -> http::Response<B> {
// ... handle the request ...
}
This can be used in conjunction with skip
or skip_all
to record only
some fields of a struct:
// Remember the struct with the very large `data` field from the earlier
// example? Now it also has a `name`, which we might want to include in
// our span.
#[derive(Debug)]
struct MyType {
name: &'static str,
data: Vec<u8>,
}
impl MyType {
// This will skip the `data` field, but will include `self.name`,
// formatted using `fmt::Display`.
#[instrument(skip(self), fields(self.name = %self.name))]
pub fn my_method(&mut self, an_interesting_argument: usize) {
// ... do something (hopefully, using all that `data`!)
}
}
Adding an empty field to be recorded later:
// This function does a very interesting and important mathematical calculation.
// Suppose we want to record both the inputs to the calculation *and* its result...
#[instrument(fields(result))]
pub fn do_calculation(input_1: usize, input_2: usize) -> usize {
// Rerform the calculation.
let result = input_1 + input_2;
// Record the result as part of the current span.
tracing::Span::current().record("result", &result);
// Now, the result will also be included on this event!
tracing::info!("calculation complete!");
// ... etc ...
}
Examples
Instrumenting a function:
#[instrument]
pub fn my_function(my_arg: usize) {
// This event will be recorded inside a span named `my_function` with the
// field `my_arg`.
tracing::info!("inside my_function!");
// ...
}
Setting the level for the generated span:
#[instrument(level = Level::DEBUG)]
pub fn my_function() {
// ...
}
Levels can be specified either with Level
constants, literal strings
(e.g., "debug"
, "info"
) or numerically (1—5, corresponding to Level::TRACE
—Level::ERROR
).
Overriding the generated span’s name:
#[instrument(name = "my_name")]
pub fn my_function() {
// ...
}
Overriding the generated span’s target:
#[instrument(target = "my_target")]
pub fn my_function() {
// ...
}
Overriding the generated span’s parent:
#[instrument(parent = None)]
pub fn my_function() {
// ...
}
// A struct which owns a span handle.
struct MyStruct
{
span: tracing::Span
}
impl MyStruct
{
// Use the struct's `span` field as the parent span
#[instrument(parent = &self.span, skip(self))]
fn my_method(&self) {}
}
Specifying follows_from
relationships:
#[instrument(follows_from = causes)]
pub fn my_function(causes: &[tracing::Id]) {
// ...
}
Any expression of type impl IntoIterator<Item = impl Into<Option<Id>>>
may be provided to follows_from
; e.g.:
#[instrument(follows_from = [cause])]
pub fn my_function(cause: &tracing::span::EnteredSpan) {
// ...
}
To skip recording an argument, pass the argument’s name to the skip
:
struct NonDebug;
#[instrument(skip(non_debug))]
fn my_function(arg: usize, non_debug: NonDebug) {
// ...
}
To add additional context to the span, pass key-value pairs to fields
:
#[instrument(fields(foo="bar", id=1, show=true))]
fn my_function(arg: usize) {
// ...
}
Adding the ret
argument to #[instrument]
will emit an event with the function’s
return value when the function returns:
#[instrument(ret)]
fn my_function() -> i32 {
42
}
The return value event will have the same level as the span generated by #[instrument]
.
By default, this will be INFO
, but if the level is overridden, the event will be at the same
level.
It’s also possible to override the level for the ret
event independently:
#[instrument(ret(level = Level::WARN))]
fn my_function() -> i32 {
42
}
Note: if the function returns a Result<T, E>
, ret
will record returned values if and
only if the function returns [Result::Ok
].
By default, returned values will be recorded using their [std::fmt::Debug
] implementations.
If a returned value implements [std::fmt::Display
], it can be recorded using its Display
implementation instead, by writing ret(Display)
:
#[instrument(ret(Display))]
fn my_function() -> i32 {
42
}
If the function returns a Result<T, E>
and E
implements std::fmt::Display
, adding
err
or err(Display)
will emit error events when the function returns Err
:
#[instrument(err)]
fn my_function(arg: usize) -> Result<(), std::io::Error> {
Ok(())
}
The level of the error value event defaults to ERROR
.
Similarly, overriding the level of the err
event :
#[instrument(err(level = Level::INFO))]
fn my_function(arg: usize) -> Result<(), std::io::Error> {
Ok(())
}
By default, error values will be recorded using their std::fmt::Display
implementations.
If an error implements std::fmt::Debug
, it can be recorded using its Debug
implementation
instead by writing err(Debug)
:
#[instrument(err(Debug))]
fn my_function(arg: usize) -> Result<(), std::io::Error> {
Ok(())
}
If a target
is specified, both the ret
and err
arguments will emit outputs to
the declared target (or the default channel if target
is not specified).
The ret
and err
arguments can be combined in order to record an event if a
function returns [Result::Ok
] or [Result::Err
]:
#[instrument(err, ret)]
fn my_function(arg: usize) -> Result<(), std::io::Error> {
Ok(())
}
async fn
s may also be instrumented:
#[instrument]
pub async fn my_function() -> Result<(), ()> {
// ...
}
It also works with async-trait (a crate that allows defining async functions in traits, something not currently possible in Rust), and hopefully most libraries that exhibit similar behaviors:
use async_trait::async_trait;
#[async_trait]
pub trait Foo {
async fn foo(&self, arg: usize);
}
#[derive(Debug)]
struct FooImpl(usize);
#[async_trait]
impl Foo for FooImpl {
#[instrument(fields(value = self.0, tmp = std::any::type_name::<Self>()))]
async fn foo(&self, arg: usize) {}
}
const fn
cannot be instrumented, and will result in a compilation failure:
#[instrument]
const fn my_const_function() {}