Granular Metrics
Granular metrics let Testkube store tool-specific measurements as named time series, so you can track more than workflow status, duration, and resource usage. Granular metrics can be used to track metrics within a workflow (e.g. the performance of an individual test case) or as summary metrics for a workflow (e.g. maximum HTTP response time).
Configuration
Granular metrics are enabled by default. The control plane worker service ingests supported reports from workflow artifacts into time series after each execution completes.
To explicitly enable or disable ingestion, set the testkube-worker-service.granularMetrics.enabled Helm value on your deployment:
testkube-worker-service:
granularInsights:
enabled: false
Set the value to false to disable granular metrics ingestion. Insights will not show
granular measures for executions processed while ingestion is disabled.
Granular Metrics in Insights
The primary way to interact with captured granular metrics is Insights. Once granular metrics are captured, they appear as measures in an Executions Time Series analysis. Granular metrics are grouped by category to make exploration easier.
| Insights group | Example metric keys |
|---|---|
| Junit Test Case Metrics | test_duration_ms, test_count_failed, test_count_passed |
| Request Latency | http_req_duration_p95_ms, response_time_mean_ms, latency_p99_ms |
| Request Throughput | http_reqs_rate, http_request_rate, rps_mean |
| Traffic Volume | data_received_count, data_sent_count, received_kbytes_per_sec |
| Request Errors | http_req_failed_rate, error_count, errors_etimedout |
| Request Volume | http_reqs_count, sample_count, requests_completed |
| Virtual Users | vus, vusers_created, virtual_users |
| Checks & Assertions | checks_passes, checks_fails, assertions_failed |
| Custom Report Metrics | Any detected report metric that does not match one of the above groups |
Insights chooses a default aggregate based on the selected metric key. Count-style metrics usually default to sum, while latency, rate, percentage, throughput, and byte-rate metrics default to maximum so the first chart highlights the highest observed value in each time bucket. You can still select another available aggregate when a different view is more useful.
Segmentation and Filtering
In addition to standard filtering and segmentation, granular metrics support segmentation and filtering based on a set of identity fields. Each series includes identity fields derived from the tool that produced the report. Below is a table of common identity fields for the supported report sources.
| Report source | Common identity fields | Notes |
|---|---|---|
| JUnit report | Testcase, Testsuite | Test cases and suites are uniquely identified by their name. If you have the same test case name across multiple workflows they will be aggregated together (a workflow filter can help mitigate this issue). |
| k6 summary | Scenario, Endpoint, k6 metric tags | k6 only writes tagged submetrics to the summary when the script requests them, usually through thresholds. Global metrics such as total received bytes or max VUs may not have scenario or endpoint identity. |
| Artillery report | Scenario, Transaction | Scenario identities come from named Artillery scenarios. Transaction identities come from endpoint-level metrics when the report includes them. Other aggregate metrics remain workflow-level when the report does not include scenario or transaction context. |
| JMeter statistics | Transaction | JMeter dashboard statistics are keyed by transaction label, including Total. |
Use Case: Granular Metrics for Test Results
Granular metrics are automatically ingested for JUnit output when you create a JUnit report. These are exposed under the
Junit Test Case Metrics measure group. For each test case and test suite in a workflow, six series are created:
- test_count_failed: The number of tests that failed.
- test_count_passed: The number of tests that passed.
- test_count_errored: The number of tests that errored.
- test_count_skipped: The number of tests that were skipped.
- test_count_total: The total number of tests.
- test_duration_ms: The duration of the test case or test suite.
These metrics can be used to understand trends for individual tests over time. For example, viewing test_duration_ms segmented
by test case can highlight your slowest tests and tests that suddenly take significantly longer than expected.
Use Case: Performance Testing
Granular metrics integrate with major performance testing tools. k6, Artillery, and JMeter are currently supported; the sections below describe how to export reports and collect them as workflow artifacts for each tool. Ready-to-run workflows are in the Basic K6, Basic Artillery, and Basic JMeter examples.
The metrics exported by each tool will differ, but you can chart the values you care about and see how they change over time.
For example, charting http_req_duration_p95_ms segmented by Scenario helps you compare p95 latency across load profiles and spot regressions in a single scenario.
k6 Summary Integration
k6 can export a machine-readable summary with the --summary-export flag. To make that summary available as
granular metrics in Testkube:
- Export the summary JSON into an artifact directory.
- Include the JSON file in the workflow artifacts.
- Run the workflow normally.
Testkube detects k6 summary JSON files during artifact post-processing and registers them as k6.summary reports.
The control plane then ingests the report into granular metric series.
For a complete workflow that exports summary.json and uploads it as an artifact, see the
Basic K6 Example.
After the workflow finishes, the summary file remains available as a normal artifact, and Testkube stores the detected k6 metrics as granular series.
k6 Metric Names
Testkube normalizes k6 metric names and summary statistics so they are stable in Insights and API responses. Common examples include:
| k6 summary field | Testkube metric key |
|---|---|
http_req_duration.values["p(95)"] | http_req_duration_p95_ms |
http_req_duration.values.avg | http_req_duration_avg_ms |
http_reqs.values.count | http_reqs_count |
http_reqs.values.rate | http_reqs_rate |
checks.values.passes | checks_passes |
checks.values.fails | checks_fails |
iteration_duration.values.avg | iteration_duration_avg_ms |
Timing metrics are stored in milliseconds. Counter and rate metrics keep their k6 summary values.
Tagged k6 Metrics
k6 can emit tagged metric summaries, for example separate http_req_duration values for successful responses or
custom tags. Testkube preserves stable tag dimensions as series identity fields. This lets you compare the same
metric overall and for a tagged subset when the k6 summary contains those tagged metrics.
For example, a tagged k6 summary metric can produce separate series for:
http_req_duration_p95_mswith no identity fields.http_req_duration_p95_mswith an identity such asexpected_response=true.http_req_duration_p95_mswithscenario=homepageorscenario=docs, when the script defines named scenarios and asks k6 to include those scenario-tagged submetrics in the summary.http_req_duration_p95_mswithendpoint=test-workflow-with-executions-list, when the script tags requests by endpoint and asks k6 to include those endpoint-tagged submetrics in the summary.
k6 does not include every possible tag combination in summary.json automatically. If you need scenario-level or
endpoint-level series in Insights, define the tag values and add thresholds
for the metric/tag combinations you want to chart—for example,
http_req_duration{scenario:homepage} or http_reqs{endpoint:my-operation}.
For endpoint-level metrics, tag each request (for example with tags: { endpoint: "my-operation" }) and add a
matching threshold. The tag key can be any stable name, but endpoint is useful for API and UI performance tests
because it lets Insights split latency by logical operation instead of by raw URL.
Testkube stores tagged summaries as metric keys such as http_req_duration_p95_ms with identity
endpoint=test-workflow-with-executions-list, so the same measure can be segmented or filtered by Endpoint in
Insights.
Use tags intentionally. High-cardinality tags, such as unique URLs or request IDs, can create many metric series and make analysis harder.
Distributed k6 Workflows
For distributed k6 workflows, each worker should write a unique summary filename and upload it as an artifact.
This avoids workers overwriting each other's summary file. See the
Distributed K6 Example for a workflow that exports per-worker
summary-worker-*.json files alongside the HTML dashboard.
Each uploaded k6 summary report is ingested into granular metric series for the workflow execution.
k6 Troubleshooting
If k6 metrics do not appear in Insights:
- Confirm that the k6 command includes
--summary-export. - Confirm that the exported JSON file is included in
artifacts.paths. - Use a
.jsonfile extension for the summary export. - Keep the summary file non-empty and in k6 summary JSON format.
- Check the workflow execution artifacts to confirm the summary file was uploaded.
The summary file can be uploaded alongside other artifacts, such as the k6 HTML dashboard report.
Artillery Report Integration
Artillery can export a machine-readable JSON report with the -o flag. To make that report available as granular
metrics in Testkube:
- Export the Artillery report JSON into an artifact directory.
- Include the JSON file in the workflow artifacts.
- Run the workflow normally.
Testkube detects Artillery JSON files with numeric aggregate metrics under counters, rates, summaries, or
histograms during artifact post-processing and registers them with the artillery.report report type. The control
plane then ingests the report into granular metric series.
For a complete workflow that writes an Artillery JSON report with -o and uploads it as an artifact, see the
Basic Artillery Example.
After the workflow finishes, the report file remains available as a normal artifact, and Testkube stores the detected Artillery aggregate metrics as granular series.
Artillery Metric Names
Testkube normalizes Artillery aggregate metric names and statistics into stable metric keys. Common examples include:
| Artillery report field | Testkube metric key |
|---|---|
aggregate.counters["http.requests"] | http_requests |
aggregate.counters["vusers.created"] | vusers_created |
aggregate.rates["http.request_rate"] | http_request_rate |
aggregate.summaries["http.response_time"].min | http_response_time_min_ms |
aggregate.summaries["http.response_time"].p95 | http_response_time_p95_ms |
aggregate.histograms["http.response_time"].p99 | http_response_time_p99_ms |
aggregate.histograms["vusers.session_length"].p99 | vusers_session_length_p99 |
aggregate.scenariosCreated | scenarios_created |
aggregate.requestsCompleted | requests_completed |
aggregate.codes["200"] | codes_200 |
aggregate.errors.ETIMEDOUT | errors_etimedout |
aggregate.latency.p95 | latency_p95_ms |
aggregate.rps.mean | rps_mean |
aggregate.scenarioDuration.p95 | scenario_duration_p95_ms |
aggregate.counters["vusers.created_by_name.homepage"] | vusers_created with scenario=homepage |
aggregate.scenarioCounts.homepage | scenario_counts with scenario=homepage |
aggregate.summaries["plugins.metrics-by-endpoint.response_time.//"].p95 | http_response_time_p95_ms with transaction=/ |
aggregate.counters["plugins.metrics-by-endpoint.//.codes.200"] | http_codes_200 with transaction=/ |
Latency, response-time, and duration metrics are stored in milliseconds when they include a statistic, such as
min, max, median, p95, or p99. Counter and rate metrics keep their Artillery report values.
The control plane also understands common Artillery aggregate fields such as latency, rps, and
scenarioDuration when those fields are present in a report.
Artillery Segmentation
Artillery metrics can use Scenario and Transaction as identity fields when the uploaded report includes those dimensions. This lets Insights split or filter Artillery report metrics by the logical scenario or endpoint that produced the measurement.
Scenario identities are created from named Artillery scenarios. For example, a scenario named homepage can appear
in the JSON report as vusers.created_by_name.homepage, and Testkube stores the name as scenario=homepage.
Transaction identities are created from endpoint-level Artillery metrics, such as
plugins.metrics-by-endpoint.response_time.// and plugins.metrics-by-endpoint.//.codes.200. Testkube stores the
endpoint as the transaction identity, so the root endpoint // is shown as transaction=/.
Scenario and transaction identities are optional per metric. Artillery aggregate metrics that do not carry scenario
or transaction context are still stored as workflow-level series. For example, global http.response_time values are
available as overall latency metrics, while plugins.metrics-by-endpoint.response_time.<endpoint> values can be
segmented by Transaction. If a Scenario or Transaction filter returns no series for a selected Artillery metric, check
whether the uploaded report contains that identity for the metric key you are charting.
Distributed Artillery Workflows
For distributed Artillery workflows, each worker should write a unique JSON report filename and upload those files as artifacts. Testkube detects each uploaded Artillery report independently.
Artillery Troubleshooting
If Artillery metrics do not appear in Insights:
- Confirm that the Artillery command includes
-o <report-file>. - Confirm that the exported JSON file is included in
artifacts.paths. - Use a
.jsonfile extension for the report. - Keep the report non-empty and in Artillery aggregate JSON format, with numeric aggregate metrics under
counters,rates,summaries, orhistograms. - Check the workflow execution artifacts to confirm the report file was uploaded.
JMeter Statistics Integration
JMeter can generate a dashboard report with the -e and -o flags. That dashboard includes a machine-readable
statistics.json file. To make JMeter statistics available as granular metrics in Testkube:
- Run JMeter with
-e -o <report-directory>. - Put the report directory under an artifact directory.
- Include the report directory in the workflow artifacts.
Testkube detects JMeter dashboard statistics.json files during artifact post-processing and registers them as
jmeter.statistics reports. The control plane then ingests the report into granular metric series.
For a complete workflow that runs JMeter with -e -o and uploads the dashboard directory (including
statistics.json) as artifacts, see the Basic JMeter Example.
After the workflow finishes, the dashboard files remain available as normal artifacts, and Testkube stores the
detected JMeter statistics as granular series. The detected report file is typically
report/statistics.json or artifacts/report/statistics.json, depending on the artifact working directory.
JMeter Metric Names
Testkube normalizes JMeter dashboard statistics into stable metric keys. Common examples include:
| JMeter statistics field | Testkube metric key |
|---|---|
sampleCount | sample_count |
errorCount | error_count |
errorPct | error_pct |
meanResTime | response_time_mean_ms |
medianResTime | response_time_median_ms |
minResTime | response_time_min_ms |
maxResTime | response_time_max_ms |
pct1ResTime | response_time_p90_ms |
pct2ResTime | response_time_p95_ms |
pct3ResTime | response_time_p99_ms |
throughput | throughput |
receivedKBytesPerSec | received_kbytes_per_sec |
sentKBytesPerSec | sent_kbytes_per_sec |
Response-time metrics are stored in milliseconds. JMeter transaction labels, including Total, are stored as
series identity fields so each transaction can be compared across workflow executions.
JMeter Troubleshooting
If JMeter metrics do not appear in Insights:
- Confirm that the JMeter command includes
-e -o <report-directory>. - Confirm that the generated dashboard contains
statistics.json. - Confirm that the report directory is included in
artifacts.paths. - Keep the
statistics.jsonfile non-empty and in JMeter dashboard statistics format. - Check the workflow execution artifacts to confirm the report directory was uploaded.