feat(cli): use non-blocking logging

This commit is contained in:
Félix Saparelli 2024-04-28 16:48:51 +12:00
parent ad27bb2874
commit d72362002c
No known key found for this signature in database
5 changed files with 48 additions and 27 deletions

13
Cargo.lock generated
View File

@ -3685,6 +3685,18 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-appender"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [
"crossbeam-channel",
"thiserror",
"time",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.27" version = "0.1.27"
@ -4060,6 +4072,7 @@ dependencies = [
"termcolor", "termcolor",
"tokio", "tokio",
"tracing", "tracing",
"tracing-appender",
"tracing-subscriber", "tracing-subscriber",
"tracing-test", "tracing-test",
"uuid", "uuid",

View File

@ -44,6 +44,7 @@ serde_json = "1.0.107"
tempfile = "3.8.1" tempfile = "3.8.1"
termcolor = "1.4.0" termcolor = "1.4.0"
tracing = "0.1.40" tracing = "0.1.40"
tracing-appender = "0.2.3"
which = "6.0.1" which = "6.0.1"
[dependencies.blake3] [dependencies.blake3]

View File

@ -15,6 +15,7 @@ use clap::{
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use tokio::{fs::File, io::AsyncReadExt}; use tokio::{fs::File, io::AsyncReadExt};
use tracing::{debug, info, trace, warn}; use tracing::{debug, info, trace, warn};
use tracing_appender::non_blocking::WorkerGuard;
use watchexec::{paths::PATH_SEPARATOR, sources::fs::WatchedPath}; use watchexec::{paths::PATH_SEPARATOR, sources::fs::WatchedPath};
use watchexec_signals::Signal; use watchexec_signals::Signal;
@ -1145,7 +1146,7 @@ fn expand_args_up_to_doubledash() -> Result<Vec<OsString>, std::io::Error> {
} }
#[inline] #[inline]
pub async fn get_args() -> Result<Args> { pub async fn get_args() -> Result<(Args, Option<WorkerGuard>)> {
let prearg_logs = logging::preargs(); let prearg_logs = logging::preargs();
if prearg_logs { if prearg_logs {
warn!("⚠ RUST_LOG environment variable set or hardcoded, logging options have no effect"); warn!("⚠ RUST_LOG environment variable set or hardcoded, logging options have no effect");
@ -1157,9 +1158,11 @@ pub async fn get_args() -> Result<Args> {
debug!("parsing arguments"); debug!("parsing arguments");
let mut args = Args::parse_from(args); let mut args = Args::parse_from(args);
if !prearg_logs { let log_guard = if !prearg_logs {
logging::postargs(&args.logging).await?; logging::postargs(&args.logging).await?
} } else {
None
};
// https://no-color.org/ // https://no-color.org/
if args.color == ColourMode::Auto && std::env::var("NO_COLOR").is_ok() { if args.color == ColourMode::Auto && std::env::var("NO_COLOR").is_ok() {
@ -1290,5 +1293,5 @@ pub async fn get_args() -> Result<Args> {
debug_assert!(args.workdir.is_some()); debug_assert!(args.workdir.is_some());
debug_assert!(args.project_origin.is_some()); debug_assert!(args.project_origin.is_some());
info!(?args, "got arguments"); info!(?args, "got arguments");
Ok(args) Ok((args, log_guard))
} }

View File

@ -1,16 +1,17 @@
use std::{path::PathBuf, env::var, fs::File, sync::Mutex}; use std::{env::var, io::stderr, path::PathBuf};
use clap::{ArgAction, Parser, ValueHint}; use clap::{ArgAction, Parser, ValueHint};
use miette::{IntoDiagnostic, Result}; use miette::{bail, Result};
use tokio::fs::metadata; use tokio::fs::metadata;
use tracing::{info, warn}; use tracing::{info, warn};
use tracing_appender::{non_blocking, non_blocking::WorkerGuard, rolling};
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
pub struct LoggingArgs { pub struct LoggingArgs {
/// Set diagnostic log level /// Set diagnostic log level
/// ///
/// This enables diagnostic logging, which is useful for investigating bugs or gaining more /// This enables diagnostic logging, which is useful for investigating bugs or gaining more
/// insight into faulty filters or "missing" events. Use multiple times to increase args.verbose. /// insight into faulty filters or "missing" events. Use multiple times to increase verbosity.
/// ///
/// Goes up to '-vvvv'. When submitting bug reports, default to a '-vvv' log level. /// Goes up to '-vvvv'. When submitting bug reports, default to a '-vvv' log level.
/// ///
@ -77,27 +78,30 @@ pub fn preargs() -> bool {
log_on log_on
} }
pub async fn postargs(args: &LoggingArgs) -> Result<()> { pub async fn postargs(args: &LoggingArgs) -> Result<Option<WorkerGuard>> {
if args.verbose == 0 { if args.verbose == 0 {
return Ok(()); return Ok(None);
} }
let log_file = if let Some(file) = &args.log_file { let (log_writer, guard) = if let Some(file) = &args.log_file {
let is_dir = metadata(&file).await.map_or(false, |info| info.is_dir()); let is_dir = metadata(&file).await.map_or(false, |info| info.is_dir());
let path = if is_dir { let (dir, filename) = if is_dir {
let filename = format!( (
"watchexec.{}.log", file.to_owned(),
chrono::Utc::now().format("%Y-%m-%dT%H-%M-%SZ") PathBuf::from(format!(
); "watchexec.{}.log",
file.join(filename) chrono::Utc::now().format("%Y-%m-%dT%H-%M-%SZ")
)),
)
} else if let (Some(parent), Some(file_name)) = (file.parent(), file.file_name()) {
(parent.into(), PathBuf::from(file_name))
} else { } else {
file.to_owned() bail!("Failed to determine log file name");
}; };
// TODO: use tracing-appender instead non_blocking(rolling::never(dir, filename))
Some(File::create(path).into_diagnostic()?)
} else { } else {
None non_blocking(stderr())
}; };
let mut builder = tracing_subscriber::fmt().with_env_filter(match args.verbose { let mut builder = tracing_subscriber::fmt().with_env_filter(match args.verbose {
@ -113,16 +117,16 @@ pub async fn postargs(args: &LoggingArgs) -> Result<()> {
builder = builder.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE); builder = builder.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE);
} }
match if let Some(writer) = log_file { match if args.log_file.is_some() {
builder.json().with_writer(Mutex::new(writer)).try_init() builder.json().with_writer(log_writer).try_init()
} else if args.verbose > 3 { } else if args.verbose > 3 {
builder.pretty().try_init() builder.pretty().with_writer(log_writer).try_init()
} else { } else {
builder.try_init() builder.with_writer(log_writer).try_init()
} { } {
Ok(()) => info!("logging initialised"), Ok(()) => info!("logging initialised"),
Err(e) => eprintln!("Failed to initialise logging, continuing with none\n{e}"), Err(e) => eprintln!("Failed to initialise logging, continuing with none\n{e}"),
} }
Ok(()) Ok(Some(guard))
} }

View File

@ -116,7 +116,7 @@ async fn run_completions(shell: ShellCompletion) -> Result<()> {
} }
pub async fn run() -> Result<()> { pub async fn run() -> Result<()> {
let args = args::get_args().await?; let (args, _log_guard) = args::get_args().await?;
if args.manual { if args.manual {
run_manpage(args).await run_manpage(args).await