diff --git a/crates/tower-runtime/tests/local_test.rs b/crates/tower-runtime/tests/local_test.rs index 65b6a24b..158c0758 100644 --- a/crates/tower-runtime/tests/local_test.rs +++ b/crates/tower-runtime/tests/local_test.rs @@ -33,6 +33,55 @@ async fn build_package_from_dir(dir: &PathBuf) -> Package { package } +#[tokio::test] +async fn test_running_hello_world_json_logs() { + tower_telemetry::enable_logging( + tower_telemetry::LogLevel::Debug, + tower_telemetry::LogFormat::Json, + tower_telemetry::LogDestination::Stdout, + ); + + debug!("Running 01-hello-world with JSON logs"); + let hello_world_dir = get_example_app_dir("01-hello-world"); + let package = build_package_from_dir(&hello_world_dir).await; + let (sender, mut receiver) = unbounded_channel(); + + // We need to create the package, which will load the app + let opts = StartOptions { + ctx: tower_telemetry::Context::new(), + package, + output_sender: sender, + cwd: None, + environment: "local".to_string(), + secrets: HashMap::new(), + parameters: HashMap::new(), + env_vars: HashMap::new(), + cache_dir: Some(config::default_cache_dir()) + }; + + // Start the app using the LocalApp runtime + let app = LocalApp::start(opts).await.expect("Failed to start app"); + + // The status should be running + let status = app.status().await.expect("Failed to get app status"); + assert!(status == Status::Running, "App should be running"); + + let mut outputs = Vec::new(); + while let Some(output) = receiver.recv().await { + outputs.push(output.line); + } + + let found_hello = outputs.iter().any(|line| line.contains("Hello, world!")); + assert!( + found_hello, + "Should have received 'Hello, world!' output from the application" + ); + + // check the status once more, should be done. + let status = app.status().await.expect("Failed to get app status"); + assert!(status == Status::Exited, "App should be running"); +} + #[tokio::test] async fn test_running_hello_world() { tower_telemetry::enable_logging( diff --git a/crates/tower-telemetry/src/logging.rs b/crates/tower-telemetry/src/logging.rs index e77ed376..c80b4464 100644 --- a/crates/tower-telemetry/src/logging.rs +++ b/crates/tower-telemetry/src/logging.rs @@ -1,3 +1,4 @@ +use std::io::IsTerminal; use tracing_subscriber::prelude::*; use tracing_subscriber::{filter::EnvFilter, fmt, layer::Layer, registry::Registry}; @@ -164,23 +165,66 @@ pub enum LogFormat { type BoxedFmtLayer = Box + Send + Sync>; +fn should_use_color(destination: &LogDestination) -> bool { + match destination { + LogDestination::Stdout => { + // Check if stdout is a TTY + std::io::stdout().is_terminal() + } + LogDestination::File(_) => { + // Never use color for file output + false + } + } +} + fn create_fmt_layer(format: LogFormat, destination: LogDestination) -> BoxedFmtLayer { + let use_color = should_use_color(&destination); + match destination { LogDestination::Stdout => match format { - LogFormat::Plain => Box::new(fmt::layer().event_format(fmt::format().pretty())), - LogFormat::Json => Box::new(fmt::layer().event_format(fmt::format().json())), + LogFormat::Plain => Box::new(fmt::layer().event_format( + fmt::format() + .pretty() + .with_target(false) + .with_file(false) + .with_line_number(false) + .with_ansi(use_color) + )), + LogFormat::Json => Box::new(fmt::layer().event_format( + fmt::format() + .json() + .with_target(false) + .with_file(false) + .with_line_number(false) + .with_ansi(use_color) + )), }, LogDestination::File(path) => { let file_appender = tracing_appender::rolling::daily(".", path); match format { LogFormat::Plain => Box::new( fmt::layer() - .event_format(fmt::format().pretty()) + .event_format( + fmt::format() + .pretty() + .with_target(false) + .with_file(false) + .with_line_number(false) + .with_ansi(use_color) + ) .with_writer(file_appender), ), LogFormat::Json => Box::new( fmt::layer() - .event_format(fmt::format().json()) + .event_format( + fmt::format() + .json() + .with_target(false) + .with_file(false) + .with_line_number(false) + .with_ansi(use_color) + ) .with_writer(file_appender), ), }