Skip to main content

oven_cli/
logging.rs

1use std::path::Path;
2
3use tracing_appender::non_blocking::WorkerGuard;
4use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
5
6/// Initialize stderr-only logging for commands that don't need file output.
7/// Used by prep, look, report, clean, ticket.
8pub fn init_stderr_only() {
9    tracing_subscriber::registry()
10        .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("oven_cli=info")))
11        .with(
12            fmt::layer()
13                .with_writer(std::io::stderr)
14                .with_target(false)
15                .with_timer(fmt::time::time()),
16        )
17        .init();
18}
19
20/// Initialize dual-output logging: human-readable to stderr, JSON to a file.
21///
22/// Used by `oven on`. The returned `WorkerGuard` must be held until shutdown
23/// to ensure the background writer thread flushes and stops cleanly.
24pub fn init_with_file(log_dir: &Path, verbose: bool) -> WorkerGuard {
25    let file_appender = tracing_appender::rolling::never(log_dir, "pipeline.log");
26    let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
27
28    let env_filter = if verbose {
29        EnvFilter::new("oven_cli=debug")
30    } else {
31        EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("oven_cli=info"))
32    };
33
34    tracing_subscriber::registry()
35        .with(env_filter)
36        .with(
37            fmt::layer()
38                .with_writer(std::io::stderr)
39                .with_target(false)
40                .with_timer(fmt::time::time()),
41        )
42        .with(fmt::layer().json().with_writer(non_blocking))
43        .init();
44
45    guard
46}
47
48#[cfg(test)]
49mod tests {
50    // Logging initialization is global and can only happen once per process,
51    // so we verify the functions compile and the guard pattern is sound.
52    // Full integration testing of log output is deferred to CLI integration tests.
53
54    #[test]
55    fn init_stderr_only_compiles() {
56        // Just verify the function signature and types are correct.
57        // Can't actually call it in tests because tracing::subscriber::set_global_default
58        // can only be called once per process.
59        let _ = std::io::stderr;
60    }
61
62    #[test]
63    fn guard_is_send() {
64        fn assert_send<T: Send>() {}
65        assert_send::<tracing_appender::non_blocking::WorkerGuard>();
66    }
67}