Performance Metrics
Testkube automatically collects performance metrics from 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.
For other tools, or when you need metrics that do not map to a built-in report format, use Influx Line Protocol instead.
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.
On top of the tool-specific metrics, Testkube also derives a small set of canonical, cross-tool metrics (latency, throughput, and error rate) so you can compare the same measure across k6, Artillery, and JMeter using one stable metric key.
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.
Canonical (Cross-Tool) Metrics
Each performance tool reports latency, throughput, and errors with its own field names, units, and statistics. To let you compare the same measure across k6, Artillery, and JMeter - and across executions that switched tools — Testkube also derives a small, curated set of canonical metrics when it ingests a performance report.
Canonical metrics are produced in addition to the raw per-tool series described above. The original tool-specific metrics
are always preserved; the canonical layer simply adds normalized counterparts with stable keys and consistent units. Only a
curated subset of metrics has a canonical counterpart—most raw keys (for example http_req_blocked_* or iteration_duration_*)
intentionally have none.
Canonical Metric Keys
Testkube currently derives three canonical metrics:
| Canonical metric key | Meaning | Unit |
|---|---|---|
latency_p95_ms | 95th-percentile request latency | milliseconds |
throughput_rps | Request throughput | requests per second |
errors_rate | Fraction of failed requests | 0–1 fraction |
Every canonical series records which tool produced it in a source_tool identity field (k6, artillery, or jmeter), so the
same canonical key can keep one series per tool instead of merging measurements from different tools.
How Each Tool Maps to Canonical Metrics
| Source tool | Source metric | Canonical metric | Notes |
|---|---|---|---|
| k6 | http_req_duration_p95_ms | latency_p95_ms | percentile reported by the tool |
| k6 | http_reqs_rate | throughput_rps | |
| k6 | http_req_failed_rate | errors_rate | already a 0–1 fraction |
| Artillery | latency_p95_ms (Artillery v1.x) | latency_p95_ms | percentile reported by the tool |
| Artillery | http_response_time_p95_ms (Artillery v2.x) | latency_p95_ms | percentile reported by the tool |
| Artillery | rps_mean | throughput_rps | |
| Artillery | derived: sum of errors_* ÷ requests_completed | errors_rate | computed when no direct field exists |
| JMeter | response_time_percentile_2_ms (JMeter pct2ResTime) | latency_p95_ms | p95 assumed; see Percentile Provenance |
| JMeter | throughput | throughput_rps | |
| JMeter | error_pct | errors_rate | scaled from 0–100 % to a 0–1 fraction |
Units and Scaling
Canonical metrics use one unit per metric regardless of the source tool:
- Latency is stored in milliseconds.
- Throughput is stored in requests per second.
errors_rateis a 0–1 fraction. Tools that report a percentage (for example JMeter'serrorPct, which is 0–100) are scaled by1/100so they line up with tools that already report a fraction (for example k6'shttp_req_failed).
Derived Errors Rate (Artillery)
Artillery aggregate reports do not include a single error-rate field. When a direct field is missing, Testkube derives
errors_rate by summing every raw errors_* counter (for example errors_etimedout and errors_econnrefused) and dividing by
requests_completed. For example, 10 ECONNREFUSED plus 5 ETIMEDOUT over 100 completed requests yields errors_rate = 0.15.
The derivation is skipped when requests_completed is missing or zero.
Percentile Provenance
Tools differ in how precisely they report percentiles, so each canonical latency_p95_ms series is annotated with a
percentile_source identity field:
reported— the tool reported the p95 value directly (k6 and Artillery).default_assumed— the value comes from a configurable percentile slot that defaults to p95. JMeter'spct2ResTimeis the second configured percentile, which defaults to the 95th but can be changed in JMeter's percentile configuration, so Testkube records that the p95 mapping is assumed rather than guaranteed.config_read— reserved for cases where the percentile is confirmed from the tool's configuration.
Use percentile_source to decide how strictly to compare p95 latency across tools. A default_assumed JMeter series is only a
true p95 if the JMeter percentile configuration still uses the default slots.
Distributed and Sharded Reports
Per-artifact identity fields (such as reportRef and reportFile) are intentionally dropped from canonical series. This lets
distributed runs—where each worker uploads its own report file—collapse into a single canonical series per tool, rather than one
series per uploaded file. The raw per-file series remain available if you need to inspect an individual worker.