JMeter can stream its performance metrics directly to InfluxDB, a time-series database, which can then be visualized in real-time using Grafana dashboards.
Let’s see JMeter in action, pushing data and Grafana pulling it.
Here’s a basic JMeter test plan. We’ll add a Backend Listener to send metrics to InfluxDB.
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeterVersion="5.4.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="SimpleTestPlan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_thread_groups">false</boolProp>
<elementProp name="TestPlan.userDefinedVariables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.userDefinedVariables"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">10</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">5</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<HttpSampler guiclass="HttpTestSampleGui" testclass="HttpSampler" testname="HTTP Request" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">example.com</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.do_ூredirects">false</boolProp>
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSampler>
<hashTree/>
<BackendListener guiclass="BackendListenerGui" testclass="BackendListener" testname="Backend Listener" enabled="true">
<elementProp name="resource_ந்தது" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="testplan_name" elementType="stringProp">
<stringProp name="key">testplan_name</stringProp>
<stringProp name="value">SimpleTestPlan</stringProp>
</elementProp>
<elementProp name="save_request_data" elementType="stringProp">
<stringProp name="key">save_request_data</stringProp>
<stringProp name="value">true</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="classname">org.apache.jmeter.visualizers.backend.influxdb.InfluxDBBackendListenerClient</stringProp>
<stringProp name="filename"></stringProp>
<stringProp name=" aryl_name">org.apache.jmeter.visualizers.backend.influxdb.InfluxDBBackendListenerClient</stringProp>
<stringProp name="measurement">jmeter_metrics</stringProp>
<stringProp name="influxdb_host">localhost</stringProp>
<stringProp name="influxdb_port">8086</stringProp>
<stringProp name="influxdb_database">jmeter</stringProp>
<stringProp name="influxdb_username"></stringProp>
<stringProp name="influxdb_password"></stringProp>
<stringProp name="influxdb_retention_policy"></stringProp>
<stringProp name="influxdb_connection_timeout">5000</stringProp>
<stringProp name="influxdb_request_timeout">5000</stringProp>
</BackendListener>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
This Backend Listener is configured to send metrics to localhost:8086 and store them in a database named jmeter. The measurement is set to jmeter_metrics.
Now, let’s set up InfluxDB and Grafana.
InfluxDB Setup:
- Install InfluxDB: If you don’t have it, grab it from the InfluxDB website.
- Start InfluxDB:
influxd - Create Database: Open the InfluxDB CLI and create the
jmeterdatabase:influx > CREATE DATABASE jmeter > SHOW DATABASES name: _internal name: jmeter
Grafana Setup:
- Install Grafana: Download from Grafana Labs.
- Start Grafana:
(Or however you start services on your OS).sudo systemctl start grafana-server - Access Grafana: Open your browser to
http://localhost:3000. Default login isadmin/admin(you’ll be prompted to change it). - Add Data Source:
- Click the gear icon (Configuration) on the left sidebar.
- Click "Data Sources".
- Click "Add data source".
- Select "InfluxDB".
- URL:
http://localhost:8086 - Database:
jmeter - Leave other fields as default or fill in username/password if you configured them in InfluxDB.
- Click "Save & Test". You should see "Database Connection OK".
Creating the Dashboard:
-
Create New Dashboard: Click the "+" icon on the left sidebar, then "Dashboard".
-
Add Panel: Click "Add new panel".
-
Select Data Source: Ensure your InfluxDB data source is selected.
-
InfluxQL Query: In the query editor, use InfluxQL to select metrics.
-
Requests Per Second (RPS):
FROM:jmeter_metricsWHERE:time > now() - 1m(adjust time range as needed)SELECT:COUNT(count)GROUP BY:time(10s)(adjust interval for smoother or more granular graphs)ALIAS BY:Requests Per Second
This counts the number of samples (requests) within each time interval.
-
Average Response Time:
FROM:jmeter_metricsWHERE:time > now() - 1mSELECT:MEAN(elapsed)GROUP BY:time(10s)ALIAS BY:Average Response Time (ms)
This calculates the average
elapsedtime (in milliseconds) for requests in each time interval. -
Error Rate:
FROM:jmeter_metricsWHERE:error != '' AND time > now() - 1mSELECT:COUNT(count)GROUP BY:time(10s)ALIAS BY:Errors
This counts samples where the
errorfield is not empty.
-
-
Visualization: Choose a suitable panel type (e.g., "Graph" or "Time series"). Configure axes, legends, and appearance.
-
Save Dashboard: Click the save icon at the top.
Now, run your JMeter test. You should see metrics appearing on your Grafana dashboard in near real-time.
The Backend Listener in JMeter is the key component here. It bypasses the standard JMeter listeners (like the "View Results Tree") which are primarily for analysis after a test. Instead, it formats JMeter’s internal metric data into a format InfluxDB understands and sends it over the network. The beauty is that JMeter doesn’t need to hold onto all the raw sample data; it aggregates and sends it, reducing its own memory footprint during long tests.
The jmeter_metrics measurement in InfluxDB will contain fields like count (number of samples), elapsed (response time in ms), bytes, and error (a string describing the error if one occurred). These are automatically tagged with information like thread_name, label (sampler name), success, etc., allowing for powerful filtering and aggregation in Grafana.
The most surprising thing about this setup is how little overhead the Backend Listener introduces. You might expect sending data in real-time to significantly impact your test performance, but JMeter’s InfluxDB client is optimized for this. It batches metrics and sends them asynchronously, meaning your actual request execution time is minimally affected. This allows you to monitor complex, high-throughput tests without needing to run separate analysis tools after the fact.
The next step would be to explore advanced Grafana features like templating to dynamically change test parameters or request labels, and setting up alerts based on specific metric thresholds.