3

I'm try to implement some centralized logging with the tracing crate. I can write to a rolling file with the tracing-appender crate or to a Graylog application with the following code:

let mut guards = Vec::new();
if let Some(log) = config.logs {
    if let Some(file) = log.file {
        let file_appender = tracing_appender::rolling::hourly(file.directory, file.filename);
        let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
        guards.push(guard);
        let file_logger = tracing_subscriber::fmt()
            .with_writer(non_blocking)
            .init();
    }

    if let Some(graylog) = log.graylog {
        let address: SocketAddr = graylog.host.parse().expect("Unable to parse graylog host address");
        let bg_task = Logger::builder()
            .additional_field("module_id", graylog.module_id)
            .init_tcp(address)
            .unwrap();

        tokio::spawn(bg_task);
    }
}

If the log configuration contains only one of .file or .graylog definition it works, but if I define both the application crashes at startup.

I think there is a conflict because both are trying to set default collector. There is any way to define both to accept every spans and events?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
TheGr8_Nik
  • 3,080
  • 4
  • 18
  • 33

1 Answers1

7

You can use Option<Layer> in with when composing layers:

let mut guards = Vec::new();
if let Some(log) = config.logs {
    let file_log = if let Some(file) = log.file {
        let file_appender = tracing_appender::rolling::hourly(file.directory, file.filename);
        let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
        guards.push(guard);
        Some(fmt::Layer::new().with_writer(non_blocking))
    } else {
        None
    };

    let grey_log = if let Some(graylog) = log.graylog {
        let address: SocketAddr = graylog
            .host
            .parse()
            .expect("Unable to parse graylog host address");
        let (layer, task) = tracing_gelf::Logger::builder().connect_tcp(address)?;
        tokio::spawn(task);
        Some(layer)
    } else {
        None
    };

    let mut subscriber = tracing_subscriber::registry()
        .with(EnvFilter::...)
        .with(file_log)
        .with(grey_log);
}
kmdreko
  • 42,554
  • 6
  • 57
  • 106
Netwave
  • 40,134
  • 6
  • 50
  • 93
  • @TheGr8_Nik, I'm glad I saved you some time. Because I know how hard this can be without help :) – Netwave Feb 10 '21 at 14:42
  • Well, it is documented, just not in handwritten text - `Option` implements `Layer` too: https://docs.rs/tracing-subscriber/0.2.15/tracing_subscriber/layer/trait.Layer.html#impl-Layer%3CS%3E-for-Option%3CL%3E – Cerberus Feb 10 '21 at 18:23
  • Aaaaah!, @kmdreko I solved it using this!!! – Netwave Sep 17 '22 at 11:32