Open emilk opened 1 year ago
Hayden helped me on Discord to get it to compile:
fn tracing_init() {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{filter, fmt, registry, reload};
let stdout_layer = fmt::Layer::default()
.with_filter(filter::LevelFilter::INFO)
.boxed();
let tracing_layers = vec![stdout_layer];
let (tracing_layers, reload_handle) = reload::Layer::new(tracing_layers);
registry().with(tracing_layers).init();
tracing::info!("before reload");
let reload_result = reload_handle.modify(|layers| {
let json_layer = fmt::Layer::default()
.json()
.with_filter(filter::LevelFilter::INFO)
.boxed();
(*layers).push(json_layer);
});
match reload_result {
Ok(_) => {} // Great!
Err(err) => tracing::warn!("Unable to add new layer: {}", err),
}
tracing::info!("after reload");
}
But according to Hayden, it panics at runtime, so this is still an issue.
+1, I've run into this when adding https://github.com/tokio-rs/tracing-opentelemetry to an existing app. It has an early stdout
to ensure network init issues are captured - this might be a common scenario
type SimplifiedHandle = reload::Handle<
LevelFilter,
fmt::Layer<Registry, DefaultFields, Format<Full, SystemTime>, Stdout>,
>;
lazy_static! {
static ref RELOAD_HANDLE: Option<SimplifiedHandle> = None;
}
pub fn initialize_logging(level: i32) {
// Create a LevelFilter and a reload::Layer
// let (level_filter, reload_handle) = reload::Layer::new(level_to_filter(level));
// Update the static handle.
// *RELOAD_HANDLE = Some(reload_handle);
let file_fmt_layer = fmt::Layer::default()
.with_level(false)
.with_writer(CallbackWriter)
.with_timer(WithoutTimeFormat)
.with_filter(level_to_filter(level)); // Use the same level filter
let console_fmt_layer = fmt::Layer::default()
.with_file(true)
.with_line_number(true)
.with_writer(std::io::stdout)
.with_timer(CustomTimeFormat)
.with_filter(level_to_filter(level)); // Use the same level filter
// let subscriber = Registry::default().with(level_filter);
let subscriber = Registry::default()
.with(file_fmt_layer)
.with(console_fmt_layer);
tracing::subscriber::set_global_default(subscriber).expect("Failed to set subscriber");
}
pub fn update_logging_level(level: i32) {
let level_filter = level_to_filter(level);
if let Some(reload_handle) = &*RELOAD_HANDLE {
reload_handle
.modify(|filter| *filter = level_filter)
.expect("Failed to update logging level");
}
}
fn level_to_filter(level: i32) -> LevelFilter {
match level {
0 => LevelFilter::ERROR,
1 => LevelFilter::WARN,
2 => LevelFilter::INFO,
3 => LevelFilter::DEBUG,
4 => LevelFilter::TRACE,
_ => LevelFilter::INFO,
}
}
how to save a reload_handle, need help
found correct usage 😸
lazy_static! {
static ref IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
static ref RELOAD_HANDLE: Mutex<Option<reload::Handle<filter::LevelFilter, Registry>>> =
Mutex::new(None);
}
pub fn setup_logging(level: i32) {
if IS_INITIALIZED.load(Ordering::Acquire) {
update_logging_level(level);
return;
}
let result = IS_INITIALIZED.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed);
match result {
Ok(_) => {
let (level_filter, reload_handle) = reload::Layer::new(level_to_filter(level));
set_reload_handle(reload_handle);
tracing_subscriber::registry()
.with(level_filter)
.with(
fmt::Layer::default()
.with_level(false)
.with_writer(CStringWriter)
.with_timer(WithoutTimeFormat),
)
.with(
fmt::Layer::default()
.with_file(true)
.with_line_number(true)
.with_timer(CustomTimeFormat),
)
.init();
}
Err(_) => {
// if the logging has already been initialized, just update the logging level
update_logging_level(level);
}
}
}
pub fn update_logging_level(level: i32) {
if let Some(reload_handle) = &*RELOAD_HANDLE.lock().unwrap() {
reload_handle
.modify(|filter| *filter = level_to_filter(level))
.expect("Failed to update logging level");
}
}
fn set_reload_handle(reload_handle: reload::Handle<filter::LevelFilter, Registry>) {
let mut handle = RELOAD_HANDLE.lock().unwrap();
*handle = Some(reload_handle);
}
Feature Request
Crates
tracing-subscriber
Motivation
There are many places I want to pipe my log events:
stderr
These can all be implemented as
Layer
s.The first thing I want to add is the
stderr
and file layers. The GUI and the network layers I want to add later once the GUI and network connections are up and running. That way any problems with the GUI/network setup is properly logged to stdout and to file.In other words: I want to dynamically add layers as the program runs.
Proposal
I propose some sort of simple API along the lines of
tracing_subscriber::registry().add(layer);
Prior art
My own C++ logging Loguru has a simple method for adding new log sinks (https://emilk.github.io/loguru/index.html#callbacks):
This lets different sub-modules of an application can easily add new log sinks at will.
Alternatives
As I was trying to get this work, several helpful people on Discord pointed me towards
tracing_subscriber::reload
.I've tried to get it to work, but so far I've failed. My code thus far:
There are several issues with this approach:
reload_handle
Perhaps all the above issues could be solved by an
tracing_subscriber
expert plus a little bit of documentation. I don't know - I'm not an expert :)The error message for the above code: