This code uses tracing events:
# Cargo.toml
[dependencies]
tracing = "0.1.3"
tracing-subscriber = { version = "0.2.9", features = ["chrono", "env-filter", "fmt"] }
tracing-appender = "0.1.1"
use tracing::{Level, event, };
use tracing::dispatcher::{with_default, Dispatch};
use std::thread;
use tracing_appender::rolling::{RollingFileAppender};
use tracing_appender::non_blocking::{NonBlocking, WorkerGuard};
use tracing_subscriber::fmt::SubscriberBuilder;
pub static file_appender:RollingFileAppender = tracing_appender::rolling::never("/ws/sarvi-sjc/", "prefix.log");
pub static (non_blocking, _guard:WorkerGuard):(NonBlocking:WorkerGuard) = tracing_appender::non_blocking(file_appender);
pub static subscriber:SubscriberBuilder = tracing_subscriber::FmtSubscriber::builder()
.with_max_level(Level::TRACE)
.with_writer(non_blocking)
.finish();
pub static my_dispatch = Dispatch::new(subscriber);
with_default(&my_dispatch, || {
event!(Level::INFO, "chmod(...)");
});
I want the first global static lines to be initialized and stored in a thread_local!()
so that it is initialized only once for each thread.
I should then be able to use that thread-specific Dispatch
instance to scope event subscribers. The above code is taken from inside a function and were let
statements. As static variables, one them doesn't work, and had the same problem within thread_local!()
as well.
pub static (non_blocking, _guard:WorkerGuard):(NonBlocking:WorkerGuard) = tracing_appender::non_blocking(file_appender);
error: expected identifier, found `(`
--> src/lib.rs:13:12
|
13 | pub static (non_blocking, _guard:WorkerGuard):(NonBlocking:WorkerGuard) = tracing_appender::non_blocking(file_appender);
| ^ expected identifier
The second problem was understanding how they are initialized in a thread local fashion.
Wrap your
static
declarations with thethread_local!
macro, then you can access each value using thewith
method, which will return a value unique to the thread.I made sure to store the
WorkerGuard
in thread local storage too, so that it does not go out of scope afterMY_DISPATCH
is initialized. This is because the documentation fortracing_appender::non_blocking
states:This way, the guard will be dropped when the thread exits. However, keep in mind that Rust's built-in thread local storage has some weird quirks about initialization and destruction. See the documentation for
std::thread::LocalKey
. Notably:Therefore, on the main thread, you should call
make_dispatch()
directly rather thanMY_DISPATCH
, so that it is dropped when the program exits (but note that in general, things are not guaranteed to be dropped, especially during a panic orstd::process::exit
, etc.; therefore, there is still a chance that some logs could be lost, as is the nature of most non-blocking I/O).