Changing a field's value in a `tracing` span

Trying the example in Span::record to set a new value for a field, I see that the default formatter prints both the old value and the new:

fn main() {
    tracing_subscriber::fmt().without_time().with_target(false).init();
    let span = tracing::info_span!("main", is_okay = true).entered();
    tracing::info!("before");
    span.record("is_okay", false);
    tracing::info!("after");
}

Prints:

 INFO main{is_okay=true}: before
 INFO main{is_okay=true is_okay=false}: after

Is this the expected behavior?
Is it possible to replace the field's value with the new one instead?

this is intended behavior. Unfortunately, rewriting values requires maintaining state to index, mutate and reformat the log entry. A better way would be to make your own Layer and update these values as you need to. Alternatively, I'd emit an event for each loop iteration, but I can see how it's nice to update a span value.

3 Likes

I can't say if it's expected, but it's definite surprising and unintuitive.

a span is just an ID, and Span::record() simply forward the field name and value to the Subscriber (together with the id of the span itself), it's up to the subscriber to decide what to do with the data.

the behavior you observed is the default behavior how the fmt::Layer would do it. specifically, it's handled by fmt::format::DefaultFields and fmt::format::DefaultVisitor, which directly append the formatted field name and field value to the underlying Writer.

you can change the behavior by using a different field formatter, for example, the json formatter serialize the field into a json object, so you will not get duplicated field values. you can also create your own field formatter, see fmt::SubscriberBuilder::fmt_fields().

1 Like