Performance Reporting & SLOs
Performance reporting turns raw test output into decisions: the same metrics framed as SLOs with pass/fail budgets, drift trends and root-cause context. Learn to encode SLOs as thresholds, export machine-readable verdicts, and publish them to CI and dashboards.
What is this?
Performance reporting turns raw test output into decisions: it presents the same metrics as SLOs with pass/fail budgets, drift trends and root-cause context so engineers and management can act. Reporting artifacts include the machine-readable summary, a human dashboard (Grafana), and an SLO statement like 'p95 under 250ms with under 1% errors over the load test window'. Done well, a report answers 'is this version shippable?' at a glance.
Why do you need it?
- An SLO gives every run a binary verdict against a target, which is what CI gates and release reviews actually need.
- Trend reports catch slow degradation that single runs hide — the 'it was fine in April' problem.
- Reports tied to the git SHA and environment make release comparisons reproducible and auditable.
- SLO-style error budgets create accountability: teams know how much 'bad' performance they can tolerate before acting.
- Shared dashboards align engineering and management on the same numbers instead of competing anecdotes.
Mental model
A pilot's instrument panel
A performance report
The pilot doesn't get a spreadsheet of raw telemetry; they get calibrated gauges with colored limits. A good report is the same: the key metrics, their budgets, and the alert color — nothing more.
How it works
- Define the SLOs as threshold expressions (e.g. http_req_failed rate<0.01 and http_req_duration p(95)<250).
- Run the test and export machine-readable results with --summary-export or --out json.
- Store results with metadata: git SHA, environment, load model and timestamp.
- Compute pass/fail and percentiles, and push metrics to a time-series store (InfluxDB or Prometheus) for dashboards.
- Publish the verdict — CI exit code, dashboard panel, release note — with the before/after delta.
From SLO definition to a published verdict.
Key commands
k6 run --summary-export summary.json script.jsWrite a machine-readable summary for reporting tools and release gates.
A JSON document containing metrics, thresholds and grouped results.
k6 run --out influxdb=http://localhost:8086/k6 script.jsPush every metric to InfluxDB so Grafana can render dashboards.
k6 run --out prometheus-rw=http://prometheus:9090/api/v1/write script.jsRemote-write results to Prometheus for long-term retention and alerting.
jq -r '.metrics.http_req_failed.values.rate' summary.jsonExtract a single verdict value for release gating scripts.
git rev-parse --short HEADCapture the code version to stamp into the report metadata.
a1b2c3d
Examples
Threshold budgets and a CI-consumable SLO gate
import http from "k6/http"import { sleep } from "k6" export const options = { vus: 100, duration: "10m", thresholds: { http_req_failed: [{ threshold: "rate<0.01", abortOnFail: true }], http_req_duration: ["p(95)<250", "p(99)<600", "max<2000"], iterations: ["rate>50"], },} export default function () { http.get("https://api.example.com/v1/items") sleep(1)}The SLO is encoded as thresholds: at most 1% errors (aborting the run early if breached), p95 under 250ms, p99 under 600ms, and at least 50 iterations per second. The run's exit code and --summary-export JSON then feed CI gates and dashboards without further interpretation.
In production
- CI runs the SLO test on every merge and blocks the build when the exit code is non-zero.
- Grafana dashboards overlay each nightly test's p95/p99 against the error-budget line so drift is visible.
- Release notes include the before/after percentile delta stamped with the git SHA.
- Weekly SLO review consumes the summary exports to spot capacity trends before launch events.
Best practices
- Encode every SLO as a threshold expression so the verdict needs no human interpretation.
- Stamp every report with git SHA, environment and load model before storing.
- Keep one canonical dashboard; conflicting numbers from different tools breed distrust.
- Report error rate and throughput alongside latency — a fast-but-failing system is still failing.
- Retain raw summary files so 'why did the April run fail' is answerable later.
Common mistakes
- Reporting averages and omitting percentiles, which hides the user-impacting tail.
- Sending load-test results to a different format or time zone than production metrics, so comparisons mislead.
- Overwriting result files every run and losing the history needed for trend reports.
- Giving management a single number without error rate and context, inviting wrong conclusions.
Common issues
jq: error (at summary.json:0): Cannot iterate over nullsummary.json was not written because the run was aborted or --summary-export was omitted.
Re-run with --summary-export summary.json and confirm the file exists before parsing.
InfluxDB write error: partial writeThe database is missing, full, or the field schema changed between runs.
Create the database (CREATE DATABASE k6), check retention, and keep the metric field names consistent.
CI gate passed but the dashboard shows p95 above budgetThe dashboard queried a different time range or metric resolution than the test run.
Query the exact run window and store the run's start and end timestamps in the report metadata.
Debugging tips
- When a run 'passes' but users complain, check the error budget window and percentile distribution, not just the verdict.
- Compare the report's p99 to max: a huge gap means outlier requests need tracing, not tuning.
- Verify the exporter timestamps align with server-side metrics, or dashboards will misalign spikes.
- If InfluxDB or Prometheus charts are empty, confirm the --out URL and that the write path is reachable.
Interview checkpoint
Hands-on lab
- Encode an SLO as k6 thresholds
- Export a machine-readable summary
- Make a run fail deliberately and read the exit code
- Build a simple CI-style gate script on the exit code
Publish an SLO Verdict from a Load Test
30 min · advanced
Quick quiz
Which single artifact turns a load test into an automated release gate?
A performance SLO is a target, and breaching it should always block a release.
The report shows p95 = 240ms (SLO 250ms) but http_req_failed = 4%. What is the correct action?
Cheat sheet
thresholds: http_req_duration: ['p(95)<250']Encode the SLO in codeabortOnFail: trueStop early on budget breach--summary-export summary.jsonMachine-readable verdict--out influxdb=...Stream to a time-series DB--out prometheus-rw=...Remote-write to Prometheusgit SHA in metadataReproducible reports