forked from NationTech/harmony
		
	
		
			
				
	
	
		
			205 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use chrono::Local;
 | |
| use console::style;
 | |
| use harmony::{
 | |
|     instrumentation::{self, HarmonyEvent},
 | |
|     modules::application::ApplicationFeatureStatus,
 | |
|     topology::TopologyStatus,
 | |
| };
 | |
| use log::{error, info, log_enabled};
 | |
| use std::io::Write;
 | |
| use std::sync::{Arc, Mutex};
 | |
| 
 | |
| pub fn init() -> tokio::task::JoinHandle<()> {
 | |
|     configure_logger();
 | |
|     let handle = tokio::spawn(handle_events());
 | |
| 
 | |
|     loop {
 | |
|         if instrumentation::instrument(HarmonyEvent::HarmonyStarted).is_ok() {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     handle
 | |
| }
 | |
| 
 | |
| 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");
 | |
| 
 | |
|             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() {
 | |
|     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 preparing_topology = Arc::clone(&preparing_topology);
 | |
|             let current_score = Arc::clone(¤t_score);
 | |
| 
 | |
|             async move {
 | |
|                 let mut preparing_topology = preparing_topology.lock().unwrap();
 | |
|                 let mut current_score = current_score.lock().unwrap();
 | |
| 
 | |
|                 match event {
 | |
|                     HarmonyEvent::HarmonyStarted => {}
 | |
|                     HarmonyEvent::HarmonyFinished => {
 | |
|                         let emoji = crate::theme::EMOJI_HARMONY.to_string();
 | |
|                         info!(emoji = emoji.as_str(); "Harmony completed");
 | |
|                         return false;
 | |
|                     }
 | |
|                     HarmonyEvent::TopologyStateChanged {
 | |
|                         topology,
 | |
|                         status,
 | |
|                         message,
 | |
|                     } => match status {
 | |
|                         TopologyStatus::Queued => {}
 | |
|                         TopologyStatus::Preparing => {
 | |
|                             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;
 | |
|                             if let Some(message) = message {
 | |
|                                 info!(status = "finished"; "{message}");
 | |
|                             }
 | |
|                         }
 | |
|                         TopologyStatus::Noop => {
 | |
|                             (*preparing_topology) = false;
 | |
|                             if let Some(message) = message {
 | |
|                                 info!(status = "skipped"; "{message}");
 | |
|                             }
 | |
|                         }
 | |
|                         TopologyStatus::Error => {
 | |
|                             (*preparing_topology) = false;
 | |
|                             if let Some(message) = message {
 | |
|                                 error!(status = "failed"; "{message}");
 | |
|                             }
 | |
|                         }
 | |
|                     },
 | |
|                     HarmonyEvent::InterpretExecutionStarted {
 | |
|                         execution_id: _,
 | |
|                         topology: _,
 | |
|                         interpret: _,
 | |
|                         score,
 | |
|                         message,
 | |
|                     } => {
 | |
|                         if *preparing_topology || current_score.is_some() {
 | |
|                             info!("{message}");
 | |
|                         } else {
 | |
|                             (*current_score) = Some(score.clone());
 | |
|                             let emoji = format!("{}", style(crate::theme::EMOJI_SCORE).blue());
 | |
|                             info!(emoji = emoji.as_str(); "Interpreting score: {score}...");
 | |
|                         }
 | |
|                     }
 | |
|                     HarmonyEvent::InterpretExecutionFinished {
 | |
|                         execution_id: _,
 | |
|                         topology: _,
 | |
|                         interpret: _,
 | |
|                         score,
 | |
|                         outcome,
 | |
|                     } => {
 | |
|                         if current_score.is_some() && current_score.clone().unwrap() == score {
 | |
|                             (*current_score) = None;
 | |
|                         }
 | |
| 
 | |
|                         match outcome {
 | |
|                             Ok(outcome) => match outcome.status {
 | |
|                                 harmony::interpret::InterpretStatus::SUCCESS => {
 | |
|                                     info!(status = "finished"; "{}", outcome.message);
 | |
|                                 }
 | |
|                                 harmony::interpret::InterpretStatus::NOOP => {
 | |
|                                     info!(status = "skipped"; "{}", outcome.message);
 | |
|                                 }
 | |
|                                 _ => {
 | |
|                                     error!(status = "failed"; "{}", outcome.message);
 | |
|                                 }
 | |
|                             },
 | |
|                             Err(err) => {
 | |
|                                 error!(status = "failed"; "{}", err);
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     HarmonyEvent::ApplicationFeatureStateChanged {
 | |
|                         topology: _,
 | |
|                         application,
 | |
|                         feature,
 | |
|                         status,
 | |
|                     } => match status {
 | |
|                         ApplicationFeatureStatus::Installing => {
 | |
|                             info!("Installing feature '{}' for '{}'...", feature, application);
 | |
|                         }
 | |
|                         ApplicationFeatureStatus::Installed => {
 | |
|                             info!(status = "finished"; "Feature '{}' installed", feature);
 | |
|                         }
 | |
|                         ApplicationFeatureStatus::Failed { details } => {
 | |
|                             error!(status = "failed"; "Feature '{}' installation failed: {}", feature, details);
 | |
|                         }
 | |
|                     },
 | |
|                 }
 | |
|                 true
 | |
|             }
 | |
|         }
 | |
|     })
 | |
|     .await;
 | |
| }
 |