tracing: Unable to dynamically configure with builder

Feature Request

Crates

none

Motivation

Doing anything dynamic can get pretty hairy:

let builder = SubscriberBuilder::builder();
if something {
   builder = builder.json();
} else {
   builder = builder.compact();
}

Proposal

Make the various builders use functions of the form: fn foo(&mut self, arg: T) -> &mut Self

Alternative

Could accept a with_x for every option so at least re-assignment on conditionals could be worked around (there’s nothing for Format currently)

Is there a reason why we can’t do this? Why consume self for each step in the build.

I’m happy to implement this change if it’s something you’re interested in.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 10
  • Comments: 16 (13 by maintainers)

Most upvoted comments

Not sure this is exactly the same issue, but I ran into something similar trying to config layers with cargo features and Registry. I couldn’t figure out a good way to do it without repeating .init() in multible blocks.

It would be good for there to be a example and recommended way to do this. Eventually found the answer: just wrap in an Option. https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/index.html#runtime-configuration-with-layers

Also #2499 may be related

Got a ping on this from the last message, thought I would add some more context. There are obvious workarounds to this, but if you want to see how I currently solve it in a real project: https://github.com/leshow/nailgun/blob/master/bin/src/logs.rs#L27

One use case (as above) is if you’ve got a cli that wants to output json/pretty formatted/etc output depending on some input. I’m actually totally OK with the status quo, because this is the kind of setup that you write once and don’t often touch. But at the same time I could see how it could get much worse if you wanted to expose more options at runtime, you could end up with many repetitive branches

Would that work similarly to how erased-serde works? something like:

trait ErasedSubscriber {
fn erased(&self, sub: &mut dyn Subscriber) -> Result<Ok, Error>;
}

My particular use case is a bunch of configuration values come in at runtime, this is what we have right now to support this:

 fn configure_tracing(&self) -> Result<()> {
        let json_builder = Subscriber::builder();
        let full_builder = Subscriber::builder();
        let log_lvl = self.log_lvl.clone();
        match self.log_frmt.as_str() {
            "json" => {
                let json_builder = json_builder
                    .with_max_level(log_lvl)
                    .event_format(format::Format::default().json());
                tracing::subscriber::set_global_default(json_builder.finish())
            }
            _ => {
                let full_builder = full_builder
                    .with_max_level(log_lvl)
                    .event_format(format::Format::default());
                tracing::subscriber::set_global_default(full_builder.finish())
            }
        }
        .expect("setting default tracing subscriber failed");
        Ok(())
    }

Simplifying to what you’ve got above would be a big improvement

How do you feel about adding a with_format option or something like that? I’d be happy to do it.

https://docs.rs/tracing-subscriber/0.2.0/tracing_subscriber/fmt/struct.SubscriberBuilder.html#method.event_format 😃