Elegant conditional appender? #1588
-
I want my app to always log to stdout and if the user sets an environment variable, I want to log to a file in addition. I got the desired behavior with the following code and was wondering, if there's a prettier way because I need to duplicate the subscriber creation code due to layer typing issues when trying it differently. Here's what I do: macro_rules! json_layer {
($writer:expr) => {
fmt::layer()
.json()
.with_writer($writer)
.with_timer(time::ChronoUtc::default())
.flatten_event(true)
};
}
pub(crate) fn init() -> io::Result<Vec<WorkerGuard>> {
LogTracer::init().expect("Unable to setup log tracer!");
// collect and hold on to the guards to keep loggers alive
let mut guards: Vec<WorkerGuard> = Vec::new();
// always log to stdout
let (stdout_writer, stdout_writer_guard) = tracing_appender::non_blocking(std::io::stdout());
guards.push(stdout_writer_guard);
let stdout_layer = json_layer!(stdout_writer);
// when LOG_PATH is specified and valid, write logs to a file as well
let log_path = match std::env::var("LOG_PATH") {
Ok(env) => {
let path = Path::new(&env);
if path.is_dir() && path.writable() {
let file_appender = tracing_appender::rolling::daily(path, DEFAULT_LOG_FILE_PREFIX);
let (file_writer, file_writer_guard) =
tracing_appender::non_blocking(file_appender);
let file_writer_layer = json_layer!(file_writer);
guards.push(file_writer_guard);
Some(file_writer_layer)
} else {
None
}
}
Err(e) => {
println!("Apparently you tried to specify a LOG_PATH, but it could not be validated as path to a writable directory - {:?}", e);
None
}
};
match log_path {
Some(file_appender_layer) => {
// log to stdout and file
let subscriber = Registry::default()
.with(EnvFilter::from_default_env())
.with(stdout_layer)
.with(file_appender_layer);
tracing::subscriber::set_global_default(subscriber)
.map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))?;
}
None => {
// only log to stdout
let subscriber = Registry::default()
.with(EnvFilter::from_default_env())
.with(stdout_layer);
tracing::subscriber::set_global_default(subscriber)
.map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))?;
}
}
Ok(guards)
} You would think one could simply put
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Try using the implementation of pub(crate) fn init() -> io::Result<Vec<WorkerGuard>> {
LogTracer::init().expect("Unable to setup log tracer!");
// collect and hold on to the guards to keep loggers alive
let mut guards: Vec<WorkerGuard> = Vec::new();
// always log to stdout
let (stdout_writer, stdout_writer_guard) = tracing_appender::non_blocking(std::io::stdout());
guards.push(stdout_writer_guard);
let stdout_layer = json_layer!(stdout_writer);
// when LOG_PATH is specified and valid, write logs to a file as well
let log_path = match std::env::var("LOG_PATH") {
// ...
};
let subscriber = Registry::default()
.with(EnvFilter::from_default_env())
.with(stdout_layer)
// `file_appender_layer` is an `Option<fmt::Layer<...>>`. if the file is enabled, it will
// be `Some`. otherwise, it will be `None`, and adding it to the subscriber will do
// nothing.
.with(file_appender_layer);
tracing::subscriber::set_global_default(subscriber)
.map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))?;
Ok(guards)
} |
Beta Was this translation helpful? Give feedback.
Try using the implementation of
Layer
forOption<L: Layer<S>>
, like this: