How to Visualize JMeter Performance Data with Grafana, InfluxDB, and Prometheus
This article explains step‑by‑step how to collect JMeter test metrics via Backend Listener, store them in InfluxDB, and display real‑time performance charts—including TPS, response time, and error rates—in Grafana, while also covering node_exporter integration with Prometheus for system‑level monitoring.
Overview
The article outlines a practical monitoring workflow for performance testing, focusing on the most common components: JMeter, InfluxDB, Grafana, Prometheus, and node_exporter. It explains why raw JMeter console output or HTML reports are insufficient for large‑scale analysis and introduces a data‑driven approach.
JMeter + InfluxDB + Grafana
JMeter’s Backend Listener can send metrics asynchronously to InfluxDB (or Graphite). After configuring the listener, metrics such as total requests, response time, thread count, and error rate are written to InfluxDB every 30 seconds. Grafana then uses an InfluxDB data source and a pre‑built JMeter dashboard (ID 5496) to render real‑time charts that match the console output.
<code>private void addMetrics(String transaction, SamplerMetric metric) {
// FOR ALL STATUS
addMetric(transaction, metric.getTotal(), metric.getSentBytes(), metric.getReceivedBytes(), TAG_ALL, metric.getAllMean(), metric.getAllMinTime(),
metric.getAllMaxTime(), allPercentiles.values(), metric::getAllPercentile);
// FOR OK STATUS
addMetric(transaction, metric.getSuccesses(), null, null, TAG_OK, metric.getOkMean(), metric.getOkMinTime(),
metric.getOkMaxTime(), okPercentiles.values(), metric::getOkPercentile);
// FOR KO STATUS
addMetric(transaction, metric.getFailures(), null, null, TAG_KO, metric.getKoMean(), metric.getKoMinTime(),
metric.getKoMaxTime(), koPercentiles.values(), metric::getKoPercentile);
metric.getErrors().forEach((error, count) -> addErrorMetric(transaction, error.getResponseCode(),
error.getResponseMessage(), count));
}</code> <code>@Override public void writeAndSendMetrics() {
if (!copyMetrics.isEmpty()) {
if (httpRequest == null) {
httpRequest = createRequest(url);
}
StringBuilder sb = new StringBuilder(copyMetrics.size()*35);
for (MetricTuple metric : copyMetrics) {
sb.append(metric.measurement)
.append(metric.tag)
.append(" ")
.append(metric.field)
.append(" ")
.append(metric.timestamp+"000000")
.append("\n");
}
StringEntity entity = new StringEntity(sb.toString(), StandardCharsets.UTF_8);
httpRequest.setEntity(entity);
lastRequest = httpClient.execute(httpRequest, new FutureCallback<HttpResponse>() {
@Override public void completed(final HttpResponse response) {
int code = response.getStatusLine().getStatusCode();
if (MetricUtils.isSuccessCode(code)) {
if (log.isDebugEnabled()) {
log.debug("Success, number of metrics written: {}", copyMetrics.size());
}
} else {
log.error("Error writing metrics to influxDB Url: {}, responseCode: {}, responseBody: {}", url, code, getBody(response));
}
}
@Override public void failed(final Exception ex) { log.error("failed to send data to influxDB server : {}", ex.getMessage()); }
@Override public void cancelled() { log.warn("Request to influxDB server was cancelled"); }
});
}
}</code>These snippets show how JMeter metrics are transformed into InfluxDB line protocol and sent via HTTP.
InfluxDB Storage Structure
Two measurements are created:
events(test‑level metadata) and
jmeter(transaction statistics). Queries in Grafana retrieve data from the
jmetermeasurement to plot throughput and percentile response‑time curves.
Grafana Configuration
Configure an InfluxDB data source, import the official JMeter dashboard, and select the appropriate measurement. The dashboard displays total TPS, 95th‑percentile response time, and per‑transaction statistics in real time.
node_exporter + Prometheus + Grafana
For system‑level monitoring, node_exporter exposes OS counters (CPU, memory, disk, etc.) which Prometheus scrapes. After adding a static job for the node_exporter endpoint in
prometheus.yml, Grafana uses a node_exporter template (ID 11074) to visualize metrics such as CPU usage, memory, and network I/O.
<code>- job_name: 's1'
static_configs:
- targets: ['172.17.211.143:9200']
</code>Example Prometheus query for CPU usage:
<code>avg(irate(node_cpu_seconds_total{instance=~"$node",mode="system"}[30m])) by (instance)
+ avg(irate(node_cpu_seconds_total{instance=~"$node",mode="user"}[30m])) by (instance)
+ avg(irate(node_cpu_seconds_total{instance=~"$node",mode="iowait"}[30m])) by (instance)
+ 1 - avg(irate(node_cpu_seconds_total{instance=~"$node",mode="idle"}[30m])) by (instance)
</code>The query combines system, user, and iowait CPU time and subtracts idle time to obtain overall CPU utilization.
Summary
The guide demonstrates how to replace manual JMeter HTML reports with an automated pipeline: JMeter → InfluxDB → Grafana for application‑level metrics, and node_exporter → Prometheus → Grafana for OS‑level metrics. Understanding the data source and meaning of each counter is essential for accurate performance analysis.
Efficient Ops
This public account is maintained by Xiaotianguo and friends, regularly publishing widely-read original technical articles. We focus on operations transformation and accompany you throughout your operations career, growing together happily.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.