diff --git a/schema.json b/schema.json index a9643b43..0600cc42 100644 --- a/schema.json +++ b/schema.json @@ -56,6 +56,54 @@ "sizePerSec" ] }, + "metrics": { + "description": "CI friendly statistics", + "type": "object", + "properties": { + "success_rate": { + "description": "The number of success requests / All requests which isn't includes deadline", + "type": "number" + }, + "requests_per_sec": { + "description": "The number of requests per second", + "type": "number" + }, + "latency_ms": { + "description": "Selected latency statistics in millieseconds", + "type": "object", + "properties": { + "min": { + "type": "number" + }, + "mean": { + "type": "number" + }, + "p50": { + "type": "number" + }, + "p95": { + "type": "number" + }, + "p99": { + "type": "number" + } + }, + "required": [ + "min", + "mean", + "p50", + "p95", + "p99", + "max" + ] + } + }, + "required": [ + "success_rate", + "requests_per_sec", + "latency_ms" + ] + }, "responseTimeHistogram": { "description": "The histogram of response time in seconds. The key is the response time in seconds and the value is the number of requests", "type": "object", @@ -433,4 +481,4 @@ "statusCodeDistribution", "errorDistribution" ] -} \ No newline at end of file +} diff --git a/src/printer.rs b/src/printer.rs index 5587e9f4..49f7603f 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -167,6 +167,24 @@ fn print_json( size_per_sec: f64, } + #[derive(Serialize)] + struct Metrics { + success_rate: f64, + requests_per_sec: f64, + latency_ms: LatencyMs, + } + + #[derive(Serialize)] + #[serde(rename = "latency_ms")] + struct LatencyMs { + min: f64, + mean: f64, + p50: f64, + p95: f64, + p99: f64, + max: f64, + } + #[derive(Serialize)] struct Triple { average: f64, @@ -196,6 +214,7 @@ fn print_json( #[derive(Serialize)] struct Result { summary: Summary, + metrics: Metrics, #[serde(rename = "responseTimeHistogram")] response_time_histogram: BTreeMap, #[serde(rename = "latencyPercentiles")] @@ -259,7 +278,20 @@ fn print_json( .percentiles .into_iter() .map(|(p, v)| (format!("p{p}"), v)) - .collect(); + .collect::>(); + + let metrics = Metrics { + success_rate: res.success_rate(), + requests_per_sec: res.len() as f64 / total_duration.as_secs_f64(), + latency_ms: LatencyMs { + min: ms(latency_stat.min()), + mean: ms(latency_stat.mean()), + p50: ms(latency_percentiles["p50"]), + p95: ms(latency_percentiles["p95"]), + p99: ms(latency_percentiles["p99"]), + max: ms(latency_stat.max()), + }, + }; let first_byte_statistics = res.first_byte_all_statistics(); @@ -389,6 +421,7 @@ fn print_json( w, &Result { summary, + metrics, response_time_histogram, latency_percentiles, first_byte_histogram, @@ -732,6 +765,11 @@ fn percentiles(values: &mut [f64]) -> BTreeMap { .collect() } +// convert s to ms +fn ms(value: f64) -> f64 { + (value * 1000.0 * 1000.0).round() / 1000.0 +} + #[cfg(test)] mod tests { use float_cmp::assert_approx_eq;