Skip to content

Commit

Permalink
Added diagnostic-path command option and improved compatibility with …
Browse files Browse the repository at this point in the history
…older versions
  • Loading branch information
edmocosta committed May 24, 2024
1 parent 0139593 commit 63fd98e
Show file tree
Hide file tree
Showing 22 changed files with 799 additions and 171 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.3.0
- Bumped a few dependencies.
- Added a command option (`diagnostic-path`) to poll the data from a Logstash diagnostic path.
- Improved compatibility with older Logstash versions (7.x), which graph API is not supported.
- The pipeline components view now shows the plugin's pipeline usage and the dropped events percentages.
- Added a few plugin's extra details on the pipeline view.

## 0.2.0
- Added a new `flows` view built on top of the latest Logstash flow metrics.
- **BREAKING**: Renamed the `view` command to `tui`.
Expand Down
67 changes: 51 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ documentation = "https://github.com/edmocosta/tuistash"
keywords = ["logstash", "tui", "cli", "terminal"]
categories = ["command-line-utilities"]
authors = ["Edmo Vamerlatti Costa <[email protected]>"]
version = "0.2.0"
version = "0.3.0"
edition = "2021"

[dependencies]
Expand All @@ -19,12 +19,12 @@ serde_json = { version = "1.0", default-features = false }
tabled = { version = "0.14.0", features = ["std","default"] }
colored = { version = "2.0.4", default-features = false }
json_to_table = { version = "0.5.0", default-features = false }
base64 = { version = "0.21", features = ["std"], default-features = false }
base64 = { version = "0.22.0", features = ["std"], default-features = false }
rustls = { version = "0.21", features = ["dangerous_configuration"], default-features = false }
colored_json = { version = "4.1", default-features = false }
colored_json = { version = "5.0", default-features = false }
humansize = { version = "2.1", default-features = false }
humantime = { version = "2.1" }
ratatui = { version = "0.25", features = ["crossterm"] }
ratatui = { version = "0.26", features = ["crossterm", "unstable-rendered-line-info"] }
crossterm = { version = "0.27.0", default-features = false, features = ["event-stream"] }
num-format = { version = "0.4", default-features = false, features = ["with-num-bigint"] }
human_format = { version = "1.0" }
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ cargo build --release

## Usage

**The Logstash's [monitoring API](https://www.elastic.co/guide/en/logstash/current/monitoring-logstash.html) must be enabled
and accessible from the client machine.**
The Logstash's [monitoring API](https://www.elastic.co/guide/en/logstash/current/monitoring-logstash.html) must be enabled
and accessible from the client machine, unless the data is being read from a Logstash diagnostic path.

```shell
$ ./tuistash --help
Expand All @@ -55,12 +55,13 @@ Commands:
help Print this message or the help of the given subcommand(s)

Options:
--host <HOST> [default: http://localhost:9600]
--username <USERNAME>
--password <PASSWORD>
--skip-tls-verification
-h, --help Print help
-V, --version Print version
--host <HOST> [default: http://localhost:9600]
--username <USERNAME>
--password <PASSWORD>
--skip-tls-verification
-p, --diagnostic-path <DIAGNOSTIC_PATH> Read the data from a Logstash diagnostic path
-h, --help Print help
-V, --version Print version

```

Expand Down
62 changes: 56 additions & 6 deletions src/cli/api/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashMap;

use serde::Serialize;
use serde::{Deserialize, Deserializer};
use serde_json::{Map, Value};

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default)]
Expand All @@ -24,6 +25,7 @@ pub struct NodeStatsVertex {
pub events_out: i64,
pub events_in: i64,
pub duration_in_millis: u64,
pub queue_push_duration_in_millis: u64,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -185,6 +187,7 @@ pub struct PipelineStats {
pub queue: Queue,
#[serde(with = "vertices")]
pub vertices: HashMap<String, NodeStatsVertex>,
pub ephemeral_id: Option<String>,
}

fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
Expand All @@ -197,10 +200,12 @@ where
}

mod vertices {
use super::NodeStatsVertex;
use std::collections::HashMap;

use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
use std::collections::HashMap;

use super::NodeStatsVertex;

pub fn serialize<S>(
map: &HashMap<String, NodeStatsVertex>,
Expand All @@ -218,8 +223,10 @@ mod vertices {
where
D: Deserializer<'de>,
{
let mut map = HashMap::new();
for item in Vec::<NodeStatsVertex>::deserialize(deserializer)? {
let vertices = Vec::<NodeStatsVertex>::deserialize(deserializer)?;
let mut map = HashMap::with_capacity(vertices.len());

for item in vertices {
map.insert(item.id.to_string(), item);
}

Expand Down Expand Up @@ -300,13 +307,28 @@ impl Plugins {

map
}

pub fn avg_duration_in_millis_percentage(&self, pipeline_duration_in_millis: u64) -> f64 {
let all_plugins = self.all();
let duration_in_millis_sum: f64 = all_plugins
.values()
.map(|plugin| {
(plugin.events.duration_in_millis as f64 / pipeline_duration_in_millis as f64)
* 100.0
})
.sum();

duration_in_millis_sum / (all_plugins.len() as f64)
}
}

mod plugins {
use crate::api::stats::Plugin;
use std::collections::HashMap;

use serde::de::{Deserialize, Deserializer};
use serde::ser::Serializer;
use std::collections::HashMap;

use crate::api::stats::Plugin;

pub fn serialize<S>(map: &HashMap<String, Plugin>, serializer: S) -> Result<S::Ok, S::Error>
where
Expand All @@ -332,7 +354,35 @@ mod plugins {
#[serde(default)]
pub struct Plugin {
pub id: String,
pub name: Option<String>,
pub flow: Option<PluginFlow>,
pub events: Events,
#[serde(flatten)]
pub other: Map<String, Value>,
}

impl Plugin {
pub fn get_other<'a, U>(&'a self, field: &str, mapper: fn(&'a Value) -> U, default: U) -> U {
let mut split: Vec<&str> = field.split('.').rev().collect();
let mut current: &Map<String, Value> = &self.other;

while let Some(path) = split.pop() {
if let Some(value) = current.get(path) {
if split.is_empty() {
return mapper(value);
}
if let Some(map_value) = value.as_object() {
current = map_value;
} else {
return default;
}
} else {
return default;
}
}

default
}
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down
4 changes: 4 additions & 0 deletions src/cli/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub struct Cli {

#[arg(long, default_value_t = false, global = true)]
pub skip_tls_verification: bool,

/// Read the data from a Logstash diagnostic path
#[arg(long, short = 'p', global = false)]
pub diagnostic_path: Option<String>,
}

pub fn build_cli() -> Cli {
Expand Down
20 changes: 19 additions & 1 deletion src/cli/commands/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ impl DurationFormatter for u64 {
}

let duration = *self as f64 / events_count as f64;
human_format::Formatter::new().format(duration)
human_format::Formatter::new()
.format(duration)
.trim()
.to_string()
}
}

Expand All @@ -37,6 +40,8 @@ pub trait NumberFormatter {
}

fn format_number_with_decimals(&self, decimals: usize) -> String;

fn strip_number_decimals(&self, decimals: usize) -> String;
}

impl NumberFormatter for i64 {
Expand All @@ -53,10 +58,23 @@ impl NumberFormatter for i64 {
.format(*self as f64),
}
}

fn strip_number_decimals(&self, _decimals: usize) -> String {
self.to_string()
}
}

impl NumberFormatter for f64 {
fn format_number_with_decimals(&self, decimals: usize) -> String {
match self {
0.0 => "0".into(),
_ => human_format::Formatter::new()
.with_decimals(decimals)
.format(*self),
}
}

fn strip_number_decimals(&self, decimals: usize) -> String {
format!("{:.1$}", self, decimals)
}
}
Loading

0 comments on commit 63fd98e

Please sign in to comment.