6

I need to instrument spans with different names dynamically. How can I create a tracing span with dynamic naming?

use tracing; // 0.1.22

fn main() {
    let name: &'static str = "foo bar";
    let span = tracing::span!(tracing::Level::TRACE, name);
    let span_handle = span.enter();
    tracing::info!("Hello, world!");
    drop(span_handle);
}
error[E0435]: attempt to use a non-constant value in a constant
 --> src/main.rs:5:54
  |
5 |     let span = tracing::span!(tracing::Level::TRACE, name);
  |                                                      ^^^^ non-constant value

Playground

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Netwave
  • 40,134
  • 6
  • 50
  • 93
  • Could you explain what root problem led you to the intermediate conclusion that you need a dunamic span name? This reads like it could well be an xy problem. – Masklinn Jan 15 '21 at 12:16
  • @Masklinn, I have a wrapper that handles different spawning of async tasks. I want to instrument each task with a different span. Each of the tasks has a name, and I want to use that name as the span name too. – Netwave Jan 15 '21 at 12:23
  • 7
    The proper way to do that would be to have a static span name (eg. "task") with an attribute for the task name: `span!(Level::TRACE, "task", name = name)` – Jmb Jan 15 '21 at 13:16

2 Answers2

3

Instead of attempting to set the span's name dynamically, add a field:

let value = "forty-two";
let span = tracing::span!(tracing::Level::TRACE, "foo bar", value = value);

From the docs:

A span consists of fields, user-defined key-value pairs of arbitrary data that describe the context the span represents

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
1

To confirm what you're after, is it dynamic naming of your spans, ie. the name is decided at runtime? or do you just want to be able to declare the name of your spans in constants and use those when you create your spans?

If it's the former, then I don't believe you're able to, because the macro explicitly only accepts literals. However if its the latter, you can work around it via basic macros.

Playground

macro_rules! prefix_name {
    ($name:literal) => {
        concat!("prefix::", $name)
    };
}

macro_rules! span_name {
    () => {
        "foo::bar"
    };
}

Then you can use it below

fn main() {
    let span = tracing::span!(tracing::Level::TRACE, prefix_name!("foo bar"));
    let span_handle = span.enter();
    tracing::info!("Hello, world!");
    drop(span_handle);
    
    let span = tracing::span!(tracing::Level::TRACE, span_name!());
    let span_handle = span.enter();
    tracing::info!("Hello, world!");
    drop(span_handle);
}

To be honest I can only take a guess at why this works (so anyone who knows for sure, please feel free to correct me). But I would assume that macro expansion occurs prior to actually trying to compile the code. So by the time the compiler is trying to compile that span, the macro has already been expanded to the underlying literal.

Naaman
  • 11
  • 1
  • 3
  • The problem is in the macro. Internally it uses `&'static str` but those are not accepted in the macro itself. Anyway, I solve it already adding metadata to the span. – Netwave Jan 19 '21 at 12:36