Introduce a way to instrument what happens within Harmony and around Harmony (e.g. in the CLI or in Composer). The goal is to provide visual feedback to the end users and inform them of the progress of their tasks (e.g. deployment) as clearly as possible. It is important to also let them know of the outcome of their tasks (what was created, where to access stuff, etc.). <img src="https://media.discordapp.net/attachments/1295353830300713062/1400289618636574741/demo.gif?ex=688c18d5&is=688ac755&hm=2c70884aacb08f7bd15cbb65a7562a174846906718aa15294bbb238e64febbce&=" /> ## Changes ### Instrumentation architecture Extensibility and ease of use is key here, while preserving type safety as much as possible. The proposed API is quite simple: ```rs // Emit an event instrumentation::instrument( HarmonyEvent::TopologyPrepared { topology: "k8s-anywhere", outcome: Outcome::success("yay") } ); // Consume events instrumentation::subscribe("Harmony CLI Logger", async |event| { match event { HarmonyEvent::TopologyPrepared { name, outcome } => todo!(), } }); ``` #### Current limitations * this API is not very extensible, but it could be easily changed to allow end users to define custom events in addition to Harmony core events * we use a tokio broadcast channel behind the scene so only in process communication can happen, but it could be easily changed to a more flexible communication mechanism as implementation details are hidden ### `harmony_composer` VS `harmony_cli` As Harmony Composer launches commands from Harmony (CLI), they both live in different processes. And because of this, we cannot easily make all the logging happens in one place (Harmony Composer) and get rid of Harmony CLI. At least not without introducing additional complexity such as communication through a server, unix socket, etc. So for the time being, it was decided to preserve both `harmony_composer` and `harmony_cli` and let them independently log their stuff and handle their own responsibilities: * `harmony_composer`: takes care only of setting up & packaging a project, delegates everything else to `harmony_cli` * `harmony_cli`: takes care of configuring & running Harmony ### Logging & prompts * [indicatif](https://github.com/console-rs/indicatif) is used to create progress bars and track progress within Harmony, Harmony CLI, and Harmony Composer * [inquire](https://github.com/mikaelmello/inquire) is preserved, but was removed from `harmony` (core) as UI concerns shouldn't go that deep * note: for now the only prompt we had was simply deleted, we'll have to find a better way to prompt stuff in the future ## Todos * [ ] Update/Create ADRs * [ ] Continue instrumentation for missing branches * [ ] Allow instrumentation to emit and subscribe to custom events Co-authored-by: Ian Letourneau <letourneau.ian@gmail.com> Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/91 Reviewed-by: johnride <jg@nationtech.io>
51 lines
1.6 KiB
Rust
51 lines
1.6 KiB
Rust
use std::time::Duration;
|
|
|
|
use indicatif::{MultiProgress, ProgressBar};
|
|
|
|
pub fn new_section(title: String) -> MultiProgress {
|
|
let multi_progress = MultiProgress::new();
|
|
let _ = multi_progress.println(title);
|
|
|
|
multi_progress
|
|
}
|
|
|
|
pub fn add_spinner(multi_progress: &MultiProgress, message: String) -> ProgressBar {
|
|
let progress = multi_progress.add(ProgressBar::new_spinner());
|
|
|
|
progress.set_style(crate::theme::SPINNER_STYLE.clone());
|
|
progress.set_message(message);
|
|
progress.enable_steady_tick(Duration::from_millis(100));
|
|
|
|
progress
|
|
}
|
|
|
|
pub fn success(multi_progress: &MultiProgress, progress: Option<ProgressBar>, message: String) {
|
|
if let Some(progress) = progress {
|
|
multi_progress.remove(&progress)
|
|
}
|
|
|
|
let progress = multi_progress.add(ProgressBar::new_spinner());
|
|
progress.set_style(crate::theme::SUCCESS_SPINNER_STYLE.clone());
|
|
progress.finish_with_message(message);
|
|
}
|
|
|
|
pub fn error(multi_progress: &MultiProgress, progress: Option<ProgressBar>, message: String) {
|
|
if let Some(progress) = progress {
|
|
multi_progress.remove(&progress)
|
|
}
|
|
|
|
let progress = multi_progress.add(ProgressBar::new_spinner());
|
|
progress.set_style(crate::theme::ERROR_SPINNER_STYLE.clone());
|
|
progress.finish_with_message(message);
|
|
}
|
|
|
|
pub fn skip(multi_progress: &MultiProgress, progress: Option<ProgressBar>, message: String) {
|
|
if let Some(progress) = progress {
|
|
multi_progress.remove(&progress)
|
|
}
|
|
|
|
let progress = multi_progress.add(ProgressBar::new_spinner());
|
|
progress.set_style(crate::theme::SKIP_SPINNER_STYLE.clone());
|
|
progress.finish_with_message(message);
|
|
}
|