forked from NationTech/harmony
		
	Merge pull request 'fix: remove indicatif in harmony_cli to simplify logging and fixing interactions' (#109) from rip-indicatif into master
Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/109
This commit is contained in:
		
						commit
						7d027bcfc4
					
				
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1834,6 +1834,7 @@ name = "harmony_cli" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "assert_cmd", | ||||
|  "chrono", | ||||
|  "clap", | ||||
|  "console", | ||||
|  "env_logger", | ||||
|  | ||||
| @ -20,7 +20,7 @@ readme = "README.md" | ||||
| license = "GNU AGPL v3" | ||||
| 
 | ||||
| [workspace.dependencies] | ||||
| log = "0.4" | ||||
| log = { version = "0.4", features = ["kv"] } | ||||
| env_logger = "0.11" | ||||
| derive-new = "0.7" | ||||
| async-trait = "0.1" | ||||
|  | ||||
| @ -22,6 +22,7 @@ indicatif = "0.18.0" | ||||
| lazy_static = "1.5.0" | ||||
| log.workspace = true | ||||
| indicatif-log-bridge = "0.2.3" | ||||
| chrono.workspace = true | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| harmony = { path = "../harmony", features = ["testing"] } | ||||
|  | ||||
| @ -1,22 +1,17 @@ | ||||
| use chrono::Local; | ||||
| use console::style; | ||||
| use harmony::{ | ||||
|     instrumentation::{self, HarmonyEvent}, | ||||
|     modules::application::ApplicationFeatureStatus, | ||||
|     topology::TopologyStatus, | ||||
| }; | ||||
| use indicatif::MultiProgress; | ||||
| use indicatif_log_bridge::LogWrapper; | ||||
| use log::error; | ||||
| use std::{ | ||||
|     sync::{Arc, Mutex}, | ||||
|     thread, | ||||
|     time::Duration, | ||||
| }; | ||||
| 
 | ||||
| use crate::progress::{IndicatifProgressTracker, ProgressTracker}; | ||||
| use log::{error, info, log_enabled}; | ||||
| use std::io::Write; | ||||
| use std::sync::{Arc, Mutex}; | ||||
| 
 | ||||
| pub fn init() -> tokio::task::JoinHandle<()> { | ||||
|     let base_progress = configure_logger(); | ||||
|     let handle = tokio::spawn(handle_events(base_progress)); | ||||
|     configure_logger(); | ||||
|     let handle = tokio::spawn(handle_events()); | ||||
| 
 | ||||
|     loop { | ||||
|         if instrumentation::instrument(HarmonyEvent::HarmonyStarted).is_ok() { | ||||
| @ -27,28 +22,76 @@ pub fn init() -> tokio::task::JoinHandle<()> { | ||||
|     handle | ||||
| } | ||||
| 
 | ||||
| fn configure_logger() -> MultiProgress { | ||||
|     let logger = | ||||
|         env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build(); | ||||
|     let level = logger.filter(); | ||||
|     let progress = MultiProgress::new(); | ||||
| fn configure_logger() { | ||||
|     env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) | ||||
|         .format(|buf, record| { | ||||
|             let debug_mode = log_enabled!(log::Level::Debug); | ||||
|             let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S"); | ||||
| 
 | ||||
|     LogWrapper::new(progress.clone(), logger) | ||||
|         .try_init() | ||||
|         .unwrap(); | ||||
|     log::set_max_level(level); | ||||
| 
 | ||||
|     progress | ||||
|             let level = match record.level() { | ||||
|                 log::Level::Error => style("ERROR").red(), | ||||
|                 log::Level::Warn => style("WARN").yellow(), | ||||
|                 log::Level::Info => style("INFO").green(), | ||||
|                 log::Level::Debug => style("DEBUG").blue(), | ||||
|                 log::Level::Trace => style("TRACE").magenta(), | ||||
|             }; | ||||
|             if let Some(status) = record.key_values().get(log::kv::Key::from("status")) { | ||||
|                 let status = status.to_borrowed_str().unwrap(); | ||||
|                 let emoji = match status { | ||||
|                     "finished" => style(crate::theme::EMOJI_SUCCESS.to_string()).green(), | ||||
|                     "skipped" => style(crate::theme::EMOJI_SKIP.to_string()).yellow(), | ||||
|                     "failed" => style(crate::theme::EMOJI_ERROR.to_string()).red(), | ||||
|                     _ => style("".into()), | ||||
|                 }; | ||||
|                 if debug_mode { | ||||
|                     writeln!( | ||||
|                         buf, | ||||
|                         "[{} {:<5} {}] {} {}", | ||||
|                         timestamp, | ||||
|                         level, | ||||
|                         record.target(), | ||||
|                         emoji, | ||||
|                         record.args() | ||||
|                     ) | ||||
|                 } else { | ||||
|                     writeln!(buf, "[{:<5}] {} {}", level, emoji, record.args()) | ||||
|                 } | ||||
|             } else if let Some(emoji) = record.key_values().get(log::kv::Key::from("emoji")) { | ||||
|                 if debug_mode { | ||||
|                     writeln!( | ||||
|                         buf, | ||||
|                         "[{} {:<5} {}] {} {}", | ||||
|                         timestamp, | ||||
|                         level, | ||||
|                         record.target(), | ||||
|                         emoji, | ||||
|                         record.args() | ||||
|                     ) | ||||
|                 } else { | ||||
|                     writeln!(buf, "[{:<5}] {} {}", level, emoji, record.args()) | ||||
|                 } | ||||
|             } else if debug_mode { | ||||
|                 writeln!( | ||||
|                     buf, | ||||
|                     "[{} {:<5} {}] {}", | ||||
|                     timestamp, | ||||
|                     level, | ||||
|                     record.target(), | ||||
|                     record.args() | ||||
|                 ) | ||||
|             } else { | ||||
|                 writeln!(buf, "[{:<5}] {}", level, record.args()) | ||||
|             } | ||||
|         }) | ||||
|         .init(); | ||||
| } | ||||
| 
 | ||||
| async fn handle_events(base_progress: MultiProgress) { | ||||
|     let progress_tracker = Arc::new(IndicatifProgressTracker::new(base_progress.clone())); | ||||
| async fn handle_events() { | ||||
|     let preparing_topology = Arc::new(Mutex::new(false)); | ||||
|     let current_score: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None)); | ||||
| 
 | ||||
|     instrumentation::subscribe("Harmony CLI Logger", { | ||||
|         move |event| { | ||||
|             let progress_tracker = Arc::clone(&progress_tracker); | ||||
|             let preparing_topology = Arc::clone(&preparing_topology); | ||||
|             let current_score = Arc::clone(¤t_score); | ||||
| 
 | ||||
| @ -59,90 +102,57 @@ async fn handle_events(base_progress: MultiProgress) { | ||||
|                 match event { | ||||
|                     HarmonyEvent::HarmonyStarted => {} | ||||
|                     HarmonyEvent::HarmonyFinished => { | ||||
|                         progress_tracker.add_section( | ||||
|                             "harmony-summary", | ||||
|                             &format!("\n{} Harmony completed\n\n", crate::theme::EMOJI_HARMONY), | ||||
|                         ); | ||||
|                         progress_tracker.add_section("harmony-finished", "\n\n"); | ||||
|                         thread::sleep(Duration::from_millis(200)); | ||||
|                         let emoji = crate::theme::EMOJI_HARMONY.to_string(); | ||||
|                         info!(emoji = emoji.as_str(); "Harmony completed"); | ||||
|                         return false; | ||||
|                     } | ||||
|                     HarmonyEvent::TopologyStateChanged { | ||||
|                         topology, | ||||
|                         status, | ||||
|                         message, | ||||
|                     } => { | ||||
|                         let section_key = topology_key(&topology); | ||||
| 
 | ||||
|                         match status { | ||||
|                     } => match status { | ||||
|                         TopologyStatus::Queued => {} | ||||
|                         TopologyStatus::Preparing => { | ||||
|                                 progress_tracker.add_section( | ||||
|                                     §ion_key, | ||||
|                                     &format!( | ||||
|                                         "\n{} Preparing environment: {topology}...", | ||||
|                                         crate::theme::EMOJI_TOPOLOGY | ||||
|                                     ), | ||||
|                                 ); | ||||
|                             let emoji = format!("{}", style(crate::theme::EMOJI_TOPOLOGY.to_string()).yellow()); | ||||
|                             info!(emoji = emoji.as_str(); "Preparing environment: {topology}..."); | ||||
|                             (*preparing_topology) = true; | ||||
|                         } | ||||
|                         TopologyStatus::Success => { | ||||
|                             (*preparing_topology) = false; | ||||
|                                 progress_tracker.add_task(§ion_key, "topology-success", ""); | ||||
|                                 progress_tracker | ||||
|                                     .finish_task("topology-success", &message.unwrap_or("".into())); | ||||
|                             if let Some(message) = message { | ||||
|                                 info!(status = "finished"; "{message}"); | ||||
|                             } | ||||
|                         } | ||||
|                         TopologyStatus::Noop => { | ||||
|                             (*preparing_topology) = false; | ||||
|                                 progress_tracker.add_task(§ion_key, "topology-skip", ""); | ||||
|                                 progress_tracker | ||||
|                                     .skip_task("topology-skip", &message.unwrap_or("".into())); | ||||
|                             if let Some(message) = message { | ||||
|                                 info!(status = "skipped"; "{message}"); | ||||
|                             } | ||||
|                         } | ||||
|                         TopologyStatus::Error => { | ||||
|                                 progress_tracker.add_task(§ion_key, "topology-error", ""); | ||||
|                             (*preparing_topology) = false; | ||||
|                                 progress_tracker | ||||
|                                     .fail_task("topology-error", &message.unwrap_or("".into())); | ||||
|                             } | ||||
|                             if let Some(message) = message { | ||||
|                                 error!(status = "failed"; "{message}"); | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                     HarmonyEvent::InterpretExecutionStarted { | ||||
|                         execution_id: task_key, | ||||
|                         topology, | ||||
|                         execution_id: _, | ||||
|                         topology: _, | ||||
|                         interpret: _, | ||||
|                         score, | ||||
|                         message, | ||||
|                     } => { | ||||
|                         let is_key_topology = (*preparing_topology) | ||||
|                             && progress_tracker.contains_section(&topology_key(&topology)); | ||||
|                         let is_key_current_score = current_score.is_some() | ||||
|                             && progress_tracker | ||||
|                                 .contains_section(&score_key(¤t_score.clone().unwrap())); | ||||
|                         let is_key_score = progress_tracker.contains_section(&score_key(&score)); | ||||
| 
 | ||||
|                         let section_key = if is_key_topology { | ||||
|                             topology_key(&topology) | ||||
|                         } else if is_key_current_score { | ||||
|                             score_key(¤t_score.clone().unwrap()) | ||||
|                         } else if is_key_score { | ||||
|                             score_key(&score) | ||||
|                         if *preparing_topology || current_score.is_some() { | ||||
|                             info!("{message}"); | ||||
|                         } else { | ||||
|                             (*current_score) = Some(score.clone()); | ||||
|                             let key = score_key(&score); | ||||
|                             progress_tracker.add_section( | ||||
|                                 &key, | ||||
|                                 &format!( | ||||
|                                     "{} Interpreting score: {score}...", | ||||
|                                     crate::theme::EMOJI_SCORE | ||||
|                                 ), | ||||
|                             ); | ||||
|                             key | ||||
|                         }; | ||||
| 
 | ||||
|                         progress_tracker.add_task(§ion_key, &task_key, &message); | ||||
|                             let emoji = format!("{}", style(crate::theme::EMOJI_SCORE).blue()); | ||||
|                             info!(emoji = emoji.as_str(); "Interpreting score: {score}..."); | ||||
|                         } | ||||
|                     } | ||||
|                     HarmonyEvent::InterpretExecutionFinished { | ||||
|                         execution_id: task_key, | ||||
|                         execution_id: _, | ||||
|                         topology: _, | ||||
|                         interpret: _, | ||||
|                         score, | ||||
| @ -155,16 +165,17 @@ async fn handle_events(base_progress: MultiProgress) { | ||||
|                         match outcome { | ||||
|                             Ok(outcome) => match outcome.status { | ||||
|                                 harmony::interpret::InterpretStatus::SUCCESS => { | ||||
|                                     progress_tracker.finish_task(&task_key, &outcome.message); | ||||
|                                     info!(status = "finished"; "{}", outcome.message); | ||||
|                                 } | ||||
|                                 harmony::interpret::InterpretStatus::NOOP => { | ||||
|                                     progress_tracker.skip_task(&task_key, &outcome.message); | ||||
|                                     info!(status = "skipped"; "{}", outcome.message); | ||||
|                                 } | ||||
|                                 _ => { | ||||
|                                     error!(status = "failed"; "{}", outcome.message); | ||||
|                                 } | ||||
|                                 _ => progress_tracker.fail_task(&task_key, &outcome.message), | ||||
|                             }, | ||||
|                             Err(err) => { | ||||
|                                 error!("Interpret error: {err}"); | ||||
|                                 progress_tracker.fail_task(&task_key, &err.to_string()); | ||||
|                                 error!(status = "failed"; "{}", err); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| @ -173,30 +184,17 @@ async fn handle_events(base_progress: MultiProgress) { | ||||
|                         application, | ||||
|                         feature, | ||||
|                         status, | ||||
|                     } => { | ||||
|                         if let Some(score) = &(*current_score) { | ||||
|                             let section_key = score_key(score); | ||||
|                             let task_key = app_feature_key(&application, &feature); | ||||
| 
 | ||||
|                             match status { | ||||
|                     } => match status { | ||||
|                         ApplicationFeatureStatus::Installing => { | ||||
|                                     let message = format!("Feature '{}' installing...", feature); | ||||
|                                     progress_tracker.add_task(§ion_key, &task_key, &message); | ||||
|                             info!("Installing feature '{}' for '{}'...", feature, application); | ||||
|                         } | ||||
|                         ApplicationFeatureStatus::Installed => { | ||||
|                                     let message = format!("Feature '{}' installed", feature); | ||||
|                                     progress_tracker.finish_task(&task_key, &message); | ||||
|                             info!(status = "finished"; "Feature '{}' installed", feature); | ||||
|                         } | ||||
|                         ApplicationFeatureStatus::Failed { details } => { | ||||
|                                     let message = format!( | ||||
|                                         "Feature '{}' installation failed: {}", | ||||
|                                         feature, details | ||||
|                                     ); | ||||
|                                     progress_tracker.fail_task(&task_key, &message); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                             error!(status = "failed"; "Feature '{}' installation failed: {}", feature, details); | ||||
|                         } | ||||
|                     }, | ||||
|                 } | ||||
|                 true | ||||
|             } | ||||
| @ -204,15 +202,3 @@ async fn handle_events(base_progress: MultiProgress) { | ||||
|     }) | ||||
|     .await; | ||||
| } | ||||
| 
 | ||||
| fn topology_key(topology: &str) -> String { | ||||
|     format!("topology-{topology}") | ||||
| } | ||||
| 
 | ||||
| fn score_key(score: &str) -> String { | ||||
|     format!("score-{score}") | ||||
| } | ||||
| 
 | ||||
| fn app_feature_key(application: &str, feature: &str) -> String { | ||||
|     format!("app-{application}-{feature}") | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user