forked from NationTech/harmony
192 lines
7.6 KiB
Rust
192 lines
7.6 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::Mutex;
|
|
|
|
pub fn init() {
|
|
configure_logger();
|
|
handle_events();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
fn handle_events() {
|
|
let preparing_topology = Mutex::new(false);
|
|
let current_score: Mutex<Option<String>> = Mutex::new(None);
|
|
|
|
instrumentation::subscribe("Harmony CLI Logger", {
|
|
move |event| {
|
|
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");
|
|
}
|
|
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() && ¤t_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 '{feature}' for '{application}'...");
|
|
}
|
|
ApplicationFeatureStatus::Installed => {
|
|
info!(status = "finished"; "Feature '{feature}' installed");
|
|
}
|
|
ApplicationFeatureStatus::Failed { details } => {
|
|
error!(status = "failed"; "Feature '{feature}' installation failed: {details}");
|
|
}
|
|
},
|
|
}
|
|
}
|
|
});
|
|
}
|