diff --git a/skills/apitally-cli/SKILL.md b/skills/apitally-cli/SKILL.md index fad7fe8..9cc405e 100644 --- a/skills/apitally-cli/SKILL.md +++ b/skills/apitally-cli/SKILL.md @@ -78,7 +78,7 @@ All commands are run via `npx @apitally/cli `. For full details, see [r ``` 4. **Fetch data** — choose based on the question. Always read the [command reference](references/commands.md) for available options. - - **Metrics** — for questions that can be answered with aggregated metrics: traffic volume, error rates, response time trends, throughput, endpoint comparisons. Use `--group-by` and `--interval` to break down by environment, endpoint, consumer, status code, or time period. + - **Metrics** — for questions that can be answered with aggregated metrics: traffic volume, error rates, response time trends, throughput, distinct consumers, endpoint comparisons. Use `--group-by` and `--interval` to break down by environment, endpoint, consumer, status code, or time period. ``` npx @apitally/cli metrics --since "" \ diff --git a/skills/apitally-cli/references/commands.md b/skills/apitally-cli/references/commands.md index 50a2836..fab0370 100644 --- a/skills/apitally-cli/references/commands.md +++ b/skills/apitally-cli/references/commands.md @@ -112,6 +112,7 @@ Fetch aggregated metrics for an app. Outputs NDJSON to stdout by default. | --------------------- | ------- | -------------------------------------- | | `requests` | integer | Total request count | | `requests_per_minute` | float | Requests per minute | +| `consumers` | integer | Number of distinct consumers | | `bytes_received` | integer | Total bytes received | | `bytes_sent` | integer | Total bytes sent | | `client_errors` | integer | 4xx errors (excluding expected errors) | diff --git a/skills/apitally-cli/references/duckdb_tables.md b/skills/apitally-cli/references/duckdb_tables.md index c847496..927635c 100644 --- a/skills/apitally-cli/references/duckdb_tables.md +++ b/skills/apitally-cli/references/duckdb_tables.md @@ -71,6 +71,7 @@ CREATE TABLE metrics ( status_code INTEGER, requests BIGINT, requests_per_minute DOUBLE, + consumers BIGINT, bytes_received BIGINT, bytes_sent BIGINT, client_errors BIGINT, diff --git a/src/main.rs b/src/main.rs index 7e0d277..a760f49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -140,8 +140,8 @@ enum Command { /// JSON array or comma-separated list of metric names to include /// - /// Available metrics: requests, requests_per_minute, bytes_received, bytes_sent, client_errors, server_errors, error_rate, - /// response_time_p50, response_time_p75, response_time_p90, response_time_p95, response_time_p99. + /// Available metrics: requests, requests_per_minute, consumers, bytes_received, bytes_sent, client_errors, server_errors, + /// error_rate, response_time_p50, response_time_p75, response_time_p90, response_time_p95, response_time_p99. #[arg(long)] metrics: String, diff --git a/src/metrics.rs b/src/metrics.rs index 465f9a8..7e9205b 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -21,6 +21,7 @@ pub(crate) fn ensure_metrics_table(conn: &duckdb::Connection) -> Result<()> { status_code INTEGER, requests BIGINT, requests_per_minute DOUBLE, + consumers BIGINT, bytes_received BIGINT, bytes_sent BIGINT, client_errors BIGINT, @@ -33,7 +34,8 @@ pub(crate) fn ensure_metrics_table(conn: &duckdb::Connection) -> Result<()> { response_time_p99 INTEGER ); ALTER TABLE metrics ADD COLUMN IF NOT EXISTS response_time_p90 INTEGER; - ALTER TABLE metrics ADD COLUMN IF NOT EXISTS response_time_p99 INTEGER;", + ALTER TABLE metrics ADD COLUMN IF NOT EXISTS response_time_p99 INTEGER; + ALTER TABLE metrics ADD COLUMN IF NOT EXISTS consumers BIGINT;", )?; Ok(()) } @@ -178,6 +180,7 @@ mod tests { ), Field::new("method", DataType::Utf8, true), Field::new("requests", DataType::Int64, false), + Field::new("consumers", DataType::Int64, false), Field::new("error_rate", DataType::Float64, false), ])); let batch = RecordBatch::try_new( @@ -197,6 +200,7 @@ mod tests { ), Arc::new(StringArray::from(vec![Some("GET"); ROW_COUNT])), Arc::new(Int64Array::from(vec![100; ROW_COUNT])), + Arc::new(Int64Array::from(vec![3; ROW_COUNT])), Arc::new(Float64Array::from(vec![0.05; ROW_COUNT])), ], ) @@ -262,7 +266,7 @@ mod tests { 1, "2025-01-01", None, - r#"["requests","error_rate"]"#, + r#"["requests","consumers","error_rate"]"#, Some("hour"), Some(r#"["method"]"#), None, @@ -280,7 +284,7 @@ mod tests { let (count, periods, matching): (i64, i64, i64) = conn .query_row( "SELECT count(*), count(DISTINCT period_start), count(*) FILTER ( - WHERE app_id = 1 AND method = 'GET' AND requests = 100 AND error_rate = 0.05 + WHERE app_id = 1 AND method = 'GET' AND requests = 100 AND consumers = 3 AND error_rate = 0.05 AND epoch_ms(period_end) - epoch_ms(period_start) = 3600000 ) FROM metrics", [],