Listeners & Results Files

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page was written against Apache JMeter 5.6.3 (latest release on 2026-10-14; it runs on Java 8 and later, Java 17 or later is recommended), with the differences planned for JMeter 6.0 noted where they matter. JMeter 6.0 is not released: the planned changes come from xdocs/changes.xml on the master branch, need Java 17 or later (Java 21 recommended) and are described as planned, not as shipped behaviour. The JSR223 Listener example uses the Groovy engine bundled with 5.6.3. No plugin is needed for anything on this page; the plugin graph listeners from jmeter-plugins.org (Plugins Manager 2.0) are mentioned only for comparison.

The books on the market (JMeter 2.11 to 3.0) still present GUI listeners and plugin graphs as the main way to report a test. Since then, results files gained the sentBytes, Connect and IdleTime columns by default, sub-results are written to CSV, several listeners were removed, and the HTML dashboard became the reporting tool. Every field, property and default on this page is taken from the official user manual (Listeners, Glossary), the component reference (section 18.3, Listeners) and the properties reference. Where those pages disagree with the shipped 5.6.3 bin/jmeter.properties or with the source code, the page says so and gives the behaviour of 5.6.3.

The running example is the Bookshelf load test, bookshelf-load.jmx: a Thread Group named Bookshelf users with 50 threads, a 60 s ramp-up and a 660 s scheduler duration, running the journey log in, search, view a book, create a loan and view the loan against the local Bookshelf API.

What a listener does

A listener is a test element that receives every sample result in its scope after the sampler, its post-processors and its assertions have finished. What it does with each result depends on the listener: show it in a tree or a table, aggregate it into statistics, draw a graph, write it to a file, or run a script. Most listeners do several of these at once: the component reference describes them as elements that view, save and read results.

All listeners that write files write the same raw data. A View Results Tree and a Summary Report pointed at the same file name produce an identical file; only the on-screen view differs. The format and the columns of that file are controlled by the Configure button of the listener (the Sample Result Save Configuration) and, for defaults, by the jmeter.save.saveservice.* properties described below.

flowchart LR S[Sampler + post-processors + assertions] --> E((SampleEvent)) E --> L1[Listeners in scope: GUI views, reports, graphs] E --> L2[Listeners with a Filename: JTL file] E --> L3["-l top-level listener (CLI): JTL file"] E --> L4[Generate Summary Results / default summariser] E --> L5[JSR223 Listener, Save Responses to a file] E --> L6[Backend Listener: async queue to InfluxDB or Graphite]

Where a listener sits and what it sees

A listener follows the normal scoping rules, with one particularity stated in the component reference: listeners are processed at the end of the scope in which they are found. In practice:

  • a listener directly under the Test Plan sees every sample of every Thread Group, including setUp and tearDown Thread Groups;

  • a listener under a Thread Group or a controller sees only the samples below it;

  • a listener as the child of a single sampler sees only that sampler, which is the documented way to record only some samples (or wrap a set of samplers in a Simple Controller and put the listener there);

  • the listener added by -l on the command line sits above the Test Plan and therefore sees everything.

Listeners always run after assertions, so the success flag they record is final. The full execution order is on Test Plan Elements, Execution Order & Scoping.

Several listeners may write to the same file; the user manual allows it as long as they all use the same save configuration. In 5.6.3 a result is written only once per file name even if two listeners in scope point to the same file.

Listeners in GUI mode and in CLI mode

In GUI mode, every enabled listener receives the samples and updates its view while the test runs. That is useful while building a plan, and expensive during a load test, because the Swing views refresh continuously (the property jmeter.gui.refresh_period, 500 ms by default, controls how often).

In CLI mode (jmeter -n), the rule in the component reference is short: listeners do not process sample data in CLI mode, but the raw data will be saved if an output file has been configured. So:

  • the view-only part of a View Results Tree, Summary Report or graph costs nothing in CLI mode;

  • any listener whose Filename field is filled still writes its file, even a View Results Tree;

  • the -l flag adds one more file-writing listener on top of the plan, configured by the jmeter.save.saveservice.* properties only;

  • the summariser named by summariser.name (default summary) prints the progress lines to the console and the log;

  • script listeners (JSR223, BeanShell), Save Responses to a file and the Backend Listener run in both modes, because they are not views.

The official advice follows from this: run load tests in CLI mode, record results with -l to a CSV file, and use as few listeners as possible; if -l is used, the listeners in the plan can all be disabled or deleted. Analyse the file afterwards, either by loading it into a listener (see Loading a JTL file into a listener after the run) or by generating the HTML dashboard. The CLI options themselves are covered on Running Tests in CLI Mode and the dashboard on HTML Dashboard Report.

Memory cost of each listener

Listeners can use a lot of memory when there are many samples, because most of them keep a copy of every sample they display. The component reference lists the exceptions. The table summarises the 5.6.3 core listeners.

Listener Memory per sample Use during a load test? Notes

View Results Tree

Keeps every sample, with request and response data

No (debugging only)

Capped at 500 entries by view.results.tree.max_results since 3.2; the documentation says it must not be used during a load test.

View Results in Table

Keeps a row per sample

No

Uses a lot of memory, like the tree.

Graph Results

Keeps every sample

No

The documentation says it must not be used during a load test.

Assertion Results

Keeps every sample label and assertion

No

Same warning as above.

Aggregate Report, Aggregate Graph

Aggregates samples with identical elapsed time

Better loaded afterwards

Median and percentiles need the distribution, so memory grows with the number of distinct response times.

Response Time Graph

Keeps samples to plot per interval

Better loaded afterwards

A view; nothing is drawn in CLI mode.

Summary Report

Constant

Yes, in GUI debugging runs; in CLI mode it is inert unless it writes a file

No percentiles, so no distribution to keep.

Simple Data Writer

Constant (writes and forgets)

Yes

The documented low-memory way to record results; combine it with CSV.

Generate Summary Results

Constant

Yes

Mainly meant for CLI runs.

JSR223 Listener, BeanShell Listener

Constant (whatever the script keeps)

Yes, if the script is cheap

Runs in the virtual user’s thread for every sample.

Save Responses to a file

Constant, but writes one file per sample

Only with Save Failed Responses only

Disk I/O per sample.

Backend Listener

Bounded asynchronous queue

Yes

See the real-time monitoring page in this section.

To minimise memory, the documentation recommends the Simple Data Writer with the CSV format.

JTL results files

A results file, conventionally given the .jtl extension, holds one record per sample. JMeter writes it from the -l flag in CLI mode or from any listener whose Filename is set. The same file can be read back by any listener, by the HTML dashboard generator and by any tool that reads CSV.

CSV versus XML

Aspect CSV (jmeter.save.saveservice.output_format=csv, the default since 2.9) XML (output_format=xml)

Size and speed

Much smaller and cheaper to write; the official recommendation for runs with many samples.

Much larger: every sample is an element with attributes, and saved bodies are escaped.

Detail

One line per sample (and per sub-sample, see Sub-results in the CSV file); no response or request data, headers or assertion results.

Can hold request and response headers, sampler data (method, query string, cookies), response data, assertion results and nested sub-samples.

Line breaks

Cannot save any item that contains line breaks.

Any text item.

Header

First line holds the field names (print_field_names=true by default since 3.0).

The <testResults version="1.2"> root element.

Used by

HTML dashboard, every listener, spreadsheets, scripts.

Every listener; the dashboard needs CSV.

Pitfall

A header-less file can only be read back if the same jmeter.save.saveservice.* properties are set.

The file declares XML 1.0 but is written with XML 1.1 rules, so strict XML parsers fail on it; the documentation advises non-strict parsers.

For load tests, use CSV. Use XML only for short functional or debugging runs where request and response details are needed.

The default CSV file in 5.6.3

With the shipped defaults, a 5.6.3 CSV results file starts with this header (the order of columns is fixed; only the selected ones are written):

timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect

Three rows written by the Bookshelf test (one thread, a search, a book view and the Transaction Controller that groups them, plus a failing login of another thread) look like this:

1791972003000,230,GET /api/books,200,OK,Bookshelf users 1-7,text,true,,5320,226,50,50,http://localhost:8080/api/books?query=tolkien,160,0,40
1791972003530,120,GET /api/books/{id},200,OK,Bookshelf users 1-7,text,true,,2210,214,50,50,http://localhost:8080/api/books/42,105,0,0
1791972003000,350,TX Search and view,200,"Number of samples in transaction : 2, number of failing samples : 0",Bookshelf users 1-7,,true,,7530,440,50,50,,265,300,40
1791972010021,1250,POST /api/auth/login,500,Internal Server Error,Bookshelf users 1-12,text,false,,312,298,50,50,http://localhost:8080/api/auth/login,1249,0,0

Reading them:

  • the first two rows are HTTP samples on the same thread; the search opened a new connection (Connect 40 ms), the book view reused it (Connect 0);

  • the third row is the Transaction Controller’s own sample: it has no data type or URL, its response message carries the number of child samples, and because timers were not included its IdleTime holds the 300 ms spent in the timer (see IdleTime);

  • the fourth row failed because of the HTTP status: success is false but failureMessage is empty, because only assertions fill that column; a failure caused by an assertion leaves the status code as received and puts the assertion’s message in failureMessage.

The row of the Transaction Controller is written after the rows of its children, because a result is written when it is complete. Values containing the delimiter, like that response message, are quoted.

Every CSV column

The user manual lists the columns in this order: timeStamp, elapsed, label, responseCode, responseMessage, threadName, dataType, success, failureMessage, bytes, sentBytes, grpThreads, allThreads, URL, Filename, latency, connect, encoding, SampleCount, ErrorCount, Hostname, IdleTime, then any variables. The 5.6.3 writer uses the header names below and puts Connect last, after IdleTime; when reading a file, JMeter uses the header, so the order does not matter to it.

CSV header Meaning Property (jmeter.save.saveservice.*) XML attribute

timeStamp

Time of the sample in milliseconds since 1970-01-01 UTC, or in the format set by timestamp_format. With the shipped jmeter.properties it is the start of the sample (see Which time the timeStamp is).

timestamp_format (none removes it)

ts

elapsed

Elapsed time in ms, from just before sending the request to just after the last response byte (Elapsed time, latency and connect time).

time

t

label

The sampler’s name (or the Transaction Controller’s name). Statistics are grouped by it.

label

lb

responseCode

For example 200, 404; for non-HTTP samplers a sampler-specific code; for a failed connection a Java exception name.

response_code

rc

responseMessage

For example OK; for a Transaction Controller, the number of children and of failing children.

response_message

rm

threadName

<Thread Group name> <group index>-<thread index>, for example Bookshelf users 1-7.

thread_name

tn

dataType

text, bin or empty.

data_type

dt

success

true or false, after assertions.

successful

s

failureMessage

The first failing assertion’s message, if any (CSV only).

assertion_results_failure_message

none

bytes

Bytes received; for HTTP, headers plus body. A Transaction Controller sample sums its children.

bytes

by

sentBytes

Bytes sent, for samplers that compute it (added in 3.1).

sent_bytes

sby

grpThreads

Number of active threads in this sample’s Thread Group when the sample was taken.

thread_counts

ng

allThreads

Number of active threads in all Thread Groups (on this JMeter instance).

thread_counts

na

URL

The sampled URL, for samplers that have one.

url

URL element

Filename

The file written by Save Responses to a file, if used.

filename

responseFile element

Latency

Time to the first response, in ms (Elapsed time, latency and connect time). Not all samplers support it.

latency

lt

Encoding

The response data encoding.

encoding

de

SampleCount

Number of samples in the record: 1, unless samples were aggregated (for example by a statistical sample sender in distributed mode).

sample_count

sc

ErrorCount

Number of errors: 0 or 1 unless samples were aggregated. Written together with SampleCount.

sample_count

ec

Hostname

Host name of the JMeter instance that generated the sample, useful when merging distributed results.

hostname

hn

IdleTime

Milliseconds of "idle" time inside the sample, excluded from elapsed; normally 0 (IdleTime).

idle_time

it

Connect

Time to establish the connection, including the TLS handshake (Elapsed time, latency and connect time). Computed only by the HTTP Request, TCP and JDBC samplers.

connect_time

ct

"variable"

One extra column per name in sample_variables, with the header in double quotes.

sample_variables (a top-level property)

attribute named after the variable

Elapsed time, latency and connect time

These three numbers are the core of every JMeter report, and the official glossary defines them precisely.

  • Elapsed time is measured from just before sending the request to just after the last response has been received. It does not include rendering the response or running client code such as JavaScript, because JMeter is not a browser.

  • Latency is measured from just before sending the request to just after the first response has been received. It therefore includes everything needed to assemble the request and the first part of the response, which is usually more than one byte. It is meant to be closer to what a client experiences than what a protocol analyser measures on the wire.

  • Connect time is the time it took to establish the connection, including the SSL/TLS handshake. It is not subtracted from latency. On a connection error it equals the time it took to fail; for a connect timeout, roughly the timeout. Since 3.1 it is computed only for the TCP Sampler, the HTTP Request and the JDBC Request.

All three start from the same instant, so for one sample Connect ≤ Latency ≤ elapsed. The figure shows them on a request timeline, together with the idle time explained in the next section.

Request timeline showing connect time, latency and elapsed time, all measured from the sample start, and the idle time excluded from a Transaction Controller sample

A worked reading for the Bookshelf search in the figure: the connection took 40 ms, the first byte arrived 160 ms after the start and the last byte 230 ms after it. So:

  • time spent waiting for the server after connecting: Latency − Connect = 160 − 40 = 120 ms (this also includes sending the request);

  • time spent downloading the rest of the response: elapsed − Latency = 230 − 160 = 70 ms;

  • on the next request of the same thread, with keep-alive, Connect is 0 and Latency drops to about 120 ms.

Patterns that follow from these definitions:

  • High Connect on many samples means new connections are being opened all the time: keep-alive disabled, a Thread Group that resets HTTP state each iteration (the HttpClient4 default since 5.0), connection pool exhaustion in front of the server, or a slow TLS handshake.

  • High Latency with elapsed close to it means the server is slow to produce the first byte (the usual case for an API).

  • elapsed much larger than Latency means a large or slowly streamed response, or a bandwidth limit; check bytes.

  • A Transaction Controller sample reports the sum of its children’s latency and connect time, not a time to first byte; read latency only on sampler rows.

IdleTime

IdleTime is the time inside a sample that JMeter deliberately excludes from it. In 5.6.3 the elapsed time is computed as end time − start time − idle time, and latency and connect time are computed relative to the same corrected start.

For an ordinary sampler IdleTime is 0 (the user manual says "normally 0"). The case that matters in practice is the Transaction Controller when its option Include duration of timer and pre-post processors in generated sample is not selected: the controller then accumulates the gaps between its children (timer delays, pre- and post-processor time) and records them as the idle time of the transaction sample. In the figure, the transaction spans 650 ms on the wall clock, 300 ms of which are a timer, so the row shows elapsed 350 and IdleTime 300. With the option selected, IdleTime is 0 and elapsed is 650.

This is why the convention in this section is to set that option explicitly in every plan: the reported transaction time changes by the whole think time depending on it. The option is described on Logic Controllers & Transactions.

The idle_time property has defaulted to true since 3.0, so the column is present unless removed.

Which time the timeStamp is

The properties reference documents sampleresult.timestamp.start as "save the start time stamp instead of the end", with a default of false. The bin/jmeter.properties shipped with 5.6.3, however, sets it explicitly:

sampleresult.timestamp.start=true

So, unless that line is changed, timeStamp is the start of each sample. To get the end time, add elapsed (and IdleTime). The format is milliseconds since the epoch by default; timestamp_format accepts none, ms or a SimpleDateFormat pattern, applies to CSV only and is used both to write and to read files. When the column does not parse as a number, JMeter also tries yyyy/MM/dd HH:mm:ss.SSS, yyyy/MM/dd HH:mm:ss, yyyy-MM-dd HH:mm:ss.SSS, yyyy-MM-dd HH:mm:ss and MM/dd/yy HH:mm:ss. Keep the default ms when the file will feed the dashboard or a script: it is unambiguous and time-zone free.

Two related properties: sampleresult.useNanoTime (default true) uses System.nanoTime() for timing, and sampleresult.nanoThreadSleep (default 5000 ms) runs a background thread to compute the nano-time offset; a value of zero or less disables that thread.

Sub-results in the CSV file

Some samples contain sub-samples: an HTTP Request that followed redirects or downloaded embedded resources, and a Transaction Controller in Generate parent sample mode. Since 5.0, with jmeter.save.saveservice.subresults=true (the default), sub-results are also written to the CSV file, one line each. Since 5.0, sub-results are also renamed with a numeric suffix (for example POST /api/auth/login-0, POST /api/auth/login-1 for a request and its redirect); subresults.disable_renaming=true restores the old naming. To keep only parent samples in a CSV file, set:

jmeter.save.saveservice.subresults=false

Count carefully when sub-results are present: a request that follows one redirect produces three lines (the parent and two children), so the number of lines is not the number of requests the virtual users made. The HTML dashboard and the listeners understand this; a spreadsheet does not.

The XML format

The XML results file (format 2.1, unchanged in 2.2) has a testResults root with httpSample elements for HTTP samples and sample elements for other samplers. Sub-samples are nested elements. The attributes are those in the XML column of the table above (t, lt, ct, ts, s, lb, rc, rm, tn, dt, de, by, sby, ng, na, sc, ec, hn, it, plus one attribute per sample variable). Optional child elements hold the request and response headers, the sampler data (including method, queryString and cookies), the response data and the responseFile name. Only text responses can be saved as response data; images cannot.

A Bookshelf sample with response headers and data saved looks like this (shortened):

<?xml version="1.0" encoding="UTF-8"?>
<testResults version="1.2">
<httpSample t="1250" it="0" lt="1249" ct="0" ts="1791972010021" s="false" lb="POST /api/auth/login"
            rc="500" rm="Internal Server Error" tn="Bookshelf users 1-12" dt="text" by="312" sby="298"
            ng="50" na="50">
  <responseHeader class="java.lang.String">HTTP/1.1 500 Internal Server Error
Content-Type: application/json
</responseHeader>
  <responseData class="java.lang.String">{"error":"database unavailable"}</responseData>
</httpSample>
</testResults>

The Sample Result Save Configuration dialog

Every file-writing listener has a Configure button that opens the Sample Result Save Configuration dialog. Its check boxes start from the jmeter.save.saveservice.* properties and are then stored in the .jmx with the listener, so a listener configured in the GUI keeps its choices even if the properties change later. Items marked (CSV) apply only to CSV, items marked (XML) only to XML. Cookies, the method and the query string are part of Sampler Data.

The check boxes appear in this order in 5.6.3:

Check box JMX element in saveConfig Property (jmeter.save.saveservice.*) Default in 5.6.3

Save As XML

xml

output_format (csv or xml)

CSV

Save Field Names (CSV)

fieldNames

print_field_names

true

Save Time Stamp

timestamp

timestamp_format (none turns it off)

ms

Save Elapsed Time

time

time

true

Save Label

label

label

true

Save Response Code

code

response_code

true

Save Response Message

message

response_message

true

Save Thread Name

threadName

thread_name

true

Save Data Type

dataType

data_type

true

Save Success

success

successful

true

Save Assertion Failure Message

saveAssertionResultsFailureMessage

assertion_results_failure_message

true

Save received byte count

bytes

bytes

true

Save sent byte count

sentBytes

sent_bytes

true

Save Active Thread Counts

threadCounts

thread_counts

true

Save URL

url

url

true (see the note below the next table)

Save Response Filename

fileName

filename

false

Save Latency

latency

latency

true

Save Connect Time

connectTime

connect_time

true (see below)

Save Encoding

encoding

encoding

false

Save Sample and Error Counts

sampleCount

sample_count

false

Save Hostname

hostname

hostname

false

Save Idle Time

idleTime

idle_time

true

Save Request Headers (XML)

requestHeaders

requestHeaders

false

Save Sampler Data (XML)

samplerData

samplerData

false

Save Response Headers (XML)

responseHeaders

responseHeaders

false

Save Response Data (XML)

responseData

response_data

false

Save Sub Results

subresults

subresults

true

Save Assertion Results (XML)

assertions

assertions

true

Two items have no check box but are stored in saveConfig: responseDataOnError (property response_data.on_error) and assertionsResultsToSave (property assertion_results).

Every jmeter.save.saveservice.* property

These properties define the defaults of the dialog and the entire configuration of the -l file. Set them in user.properties (never edit jmeter.properties) or with -J on the command line.

Property Default Meaning

jmeter.save.saveservice.output_format

csv

csv or xml (db is listed as a legitimate value but is not supported).

jmeter.save.saveservice.assertion_results_failure_message

true

Write the failureMessage column (CSV only). Default true since 3.0.

jmeter.save.saveservice.assertion_results

none

none, first or all.

jmeter.save.saveservice.data_type

true

dataType column.

jmeter.save.saveservice.label

true

label column.

jmeter.save.saveservice.response_code

true

responseCode column.

jmeter.save.saveservice.response_data

false

Save response data; not supported for CSV.

jmeter.save.saveservice.response_data.on_error

false

Save response data (and sampler data) for failed samples only; XML only.

jmeter.save.saveservice.response_message

true

responseMessage column.

jmeter.save.saveservice.successful

true

success column.

jmeter.save.saveservice.thread_name

true

threadName column.

jmeter.save.saveservice.time

true

elapsed column.

jmeter.save.saveservice.subresults

true

Save sub-results (also to CSV since 5.0).

jmeter.save.saveservice.assertions

true

Save assertion results (XML).

jmeter.save.saveservice.latency

true

Latency column.

jmeter.save.saveservice.connect_time

true in 5.6.3

Connect column. Default true since 3.1.

jmeter.save.saveservice.samplerData

false

Sampler data (XML).

jmeter.save.saveservice.responseHeaders

false

Response headers (XML).

jmeter.save.saveservice.requestHeaders

false

Request headers (XML).

jmeter.save.saveservice.encoding

false

Encoding column.

jmeter.save.saveservice.bytes

true

bytes column.

jmeter.save.saveservice.sent_bytes

true

sentBytes column.

jmeter.save.saveservice.url

true in 5.6.3

URL column.

jmeter.save.saveservice.filename

false

Filename column.

jmeter.save.saveservice.hostname

false

Hostname column.

jmeter.save.saveservice.thread_counts

true

grpThreads and allThreads columns (default true since 2.12).

jmeter.save.saveservice.sample_count

false

SampleCount and ErrorCount columns.

jmeter.save.saveservice.idle_time

true

IdleTime column (default true since 3.0).

jmeter.save.saveservice.timestamp_format

ms

none, ms or a SimpleDateFormat pattern (CSV only).

jmeter.save.saveservice.default_delimiter

,

Field delimiter; \t for tab.

jmeter.save.saveservice.print_field_names

true

Header line in CSV (default true since 3.0).

sample_variables

not set

Comma-separated JMeter variable names to save as extra columns or attributes.

jmeter.save.saveservice.xml_pi

empty

Processing instruction written on line 2 of XML files, for example an XSL stylesheet.

jmeter.save.saveservice.base_prefix

~/

Prefix that marks a file name as relative to the directory of the .jmx.

jmeter.save.saveservice.autoflush

false

Flush after every line: less data lost on a crash, at a performance cost; false since 2.10.

Where the documentation disagrees. The properties reference gives false as the default of connect_time and url. The 5.6.3 bin/jmeter.properties comments and the 5.6.3 source both use true, the changes history records the switch of connect_time to true in 3.1, and the default header shown above contains both columns. The defaults in this table are those of 5.6.3. The properties reference also lists SESSION_ID,REFERENCE as the default of sample_variables; that value is the commented-out example in jmeter.properties, and the property is not set unless you set it.

A results configuration for Bookshelf

The defaults are already close to what a load test needs. The Bookshelf user.properties adds the search term and the book id as columns (useful to find which inputs were slow), the host name (the same file layout works for distributed runs), and makes sure nothing heavy is saved:

# user.properties (next to jmeter.properties, or passed with -q)
jmeter.save.saveservice.output_format=csv
jmeter.save.saveservice.response_data=false
jmeter.save.saveservice.subresults=true
jmeter.save.saveservice.hostname=true
jmeter.save.saveservice.timestamp_format=ms
# Extra columns, written as "term" and "bookId" at the end of each line
sample_variables=term,bookId

Never put token, password or other secrets in sample_variables: they would be written to every line of the file. The variable names are also used as XML attribute names, so they must be valid XML names, and JMeter sends the list to remote servers so that the values are available on the controller.

The CSV header then ends with ...,Hostname,IdleTime,Connect,"term","bookId". A sampler that ran before term or bookId was set writes an empty value.

File names, existing files and crashes

  • Relative paths are resolved against the current working directory (by default bin/ when JMeter is started from there). A path that starts with ~/ (or with the value of jmeter.save.saveservice.base_prefix) is relative to the directory of the .jmx file, which is the portable choice: ~/results/load.jtl.

  • Missing directories are created.

  • Functions and variables may be used in the file name, but variable references do not work in client-server (distributed) mode, because the file is created on the client, which does not run the test; functions work.

  • An existing file is appended to, not replaced. For CSV the header is not written again, so two runs end up in one file. In the GUI, resultcollector.action_if_file_exists (default ASK, alternatives APPEND and DELETE) decides what happens. In CLI mode, use a new file per run or the -f option (--forceDeleteResultFile), which deletes the existing results file and the dashboard folder before the test starts.

  • Buffering: results are buffered and flushed at the end of the test (a shutdown hook flushes on Ctrl+C or kill). If the JVM dies hard, the last lines can be lost; jmeter.save.saveservice.autoflush=true reduces that risk at a measurable cost for tests without pauses.

# One results file and one log per run, deleting leftovers of a previous run with the same name
jmeter -n -t bookshelf-load.jmx -f -l results/load-01.jtl -j results/load-01.log \
  -Jthreads=50 -Jrampup=60 -Jduration=660

Saving response data only on error

When a load test reports errors, the first question is "what did the server answer?", and a CSV file cannot say: it has no response bodies. Saving every body is out of the question at load (it multiplies file size and I/O), so the goal is to save bodies only for failed samples. There are three ways, from the most to the least recommended.

1. A second file-writing listener for errors only, in XML. Keep the main results in CSV via -l, and add a Simple Data Writer with Errors selected, configured to save as XML with response data and response headers. Because it only receives failed samples, everything it saves is about errors. In the GUI:

  1. Select the Test Plan and choose Add → Listener → Simple Data Writer; name it Errors with bodies.

  2. Set Filename to ~/results/errors.xml.

  3. Under Log/Display Only, select Errors.

  4. Click Configure and select Save As XML, Save Response Data (XML), Save Response Headers (XML) and Save Assertion Results (XML). Leave Save Request Headers (XML) and Save Sampler Data (XML) off unless needed: they contain the Authorization: Bearer header and the login body.

<ResultCollector guiclass="SimpleDataWriter" testclass="ResultCollector" testname="Errors with bodies" enabled="true">
  <boolProp name="ResultCollector.error_logging">true</boolProp>
  <objProp>
    <name>saveConfig</name>
    <value class="SampleSaveConfiguration">
      <time>true</time>
      <latency>true</latency>
      <timestamp>true</timestamp>
      <success>true</success>
      <label>true</label>
      <code>true</code>
      <message>true</message>
      <threadName>true</threadName>
      <dataType>true</dataType>
      <encoding>true</encoding>
      <assertions>true</assertions>
      <subresults>true</subresults>
      <responseData>true</responseData>
      <samplerData>false</samplerData>
      <xml>true</xml>
      <fieldNames>true</fieldNames>
      <responseHeaders>true</responseHeaders>
      <requestHeaders>false</requestHeaders>
      <responseDataOnError>false</responseDataOnError>
      <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
      <assertionsResultsToSave>0</assertionsResultsToSave>
      <bytes>true</bytes>
      <sentBytes>true</sentBytes>
      <url>true</url>
      <threadCounts>true</threadCounts>
      <idleTime>true</idleTime>
      <connectTime>true</connectTime>
    </value>
  </objProp>
  <stringProp name="filename">~/results/errors.xml</stringProp>
</ResultCollector>
<hashTree/>

This listener writes in CLI mode too, because it has a file name.

2. Save Responses to a file with "Save Failed Responses only". It writes one file per failed sample (body only) and can add the file name to the results (see Save Responses to a file). Useful for binary bodies, which XML cannot hold.

3. The response_data.on_error property for the -l file. With

jmeter.save.saveservice.output_format=xml
jmeter.save.saveservice.response_data.on_error=true

the -l file saves the response data and the sampler data of every failed sample. It works only in XML, so the whole main file becomes XML, which is the opposite of the recommendation for load tests. Keep it for short smoke runs.

Separately, the Functional Test Mode check box of the Test Plan forces all response data to be saved by every listener; it must stay off in load tests.

Listeners

The file panel common to file-writing listeners

Every listener built on the result collector (View Results Tree, View Results in Table, Summary Report, Aggregate Report, Aggregate Graph, Response Time Graph, Graph Results, Assertion Results, Simple Data Writer) has the same file panel.

Field Meaning Required JMX property

Name

Descriptive name shown in the tree.

No

testname attribute

Comments

Free text.

No

TestPlan.comments

Filename

File to write results to, or to read them from. Relative and ~/ paths as described above. Functions allowed; variables do not work in distributed mode.

No

filename

Browse…​

Opens a file chooser; choosing an existing file loads it into the listener.

No

none

Log/Display Only: Errors

Write and display only failed samples (also applies when reading a file).

No

ResultCollector.error_logging

Log/Display Only: Successes

Write and display only successful samples. If neither box is selected, all samples are processed.

No

ResultCollector.success_only_logging

Configure

Opens the Sample Result Save Configuration dialog.

No

saveConfig object property

The class in the .jmx is always ResultCollector; the guiclass attribute tells which view it is: ViewResultsFullVisualizer (View Results Tree), TableVisualizer, SummaryReport, StatVisualizer (Aggregate Report), StatGraphVisualizer (Aggregate Graph), RespTimeGraphVisualizer, GraphVisualizer, AssertionVisualizer or SimpleDataWriter.

View Results Tree (debugging only)

Add → Listener → View Results Tree. The View Results Tree shows a tree of all sample results; selecting a sample shows how long it took, its response code, the request JMeter sent and the response. It is the main debugging tool while building a plan, and the official documentation states it must not be used during a load test, because of its memory and CPU cost.

The right-hand side has these tabs:

  • Sampler result: thread name, sample start, Load time (the elapsed time), Connect Time, Latency, size in bytes with header and body sizes, sent bytes, sample and error counts, data type, response code and message, and for HTTP samples the response headers;

  • Request: the request as JMeter built it, with Raw and HTTP views (body, headers, cookies). It shows only the headers JMeter added; headers the HTTP implementation adds later, such as Host, are not shown;

  • Response data: the response body and headers, displayed with the selected renderer;

  • Assertion result (when there are assertions).

Renderers, selected in the drop-down at the bottom of the tree, change how the response is displayed or let you test an extractor expression against it:

Renderer What it does

Text

Default. Shows the text of the response; content types starting with image/, audio/ or video/ are treated as binary and not shown.

Regexp Tester

Applies a regular expression to the response with the same engine as the Regular Expression Extractor and lists the matches and groups.

Boundary Extractor Tester

Tests left and right boundaries like the Boundary Extractor.

CSS/JQuery Tester

Tests a CSS selector with the JSoup or Jodd implementation.

JSON Path Tester

Tests a JSONPath expression like the JSON Extractor.

XPath Tester

Tests an XPath query.

JSON JMESPath Tester

Tests a JMESPath expression.

HTML

Approximate rendering of an HTML response; images and style sheets are not downloaded.

HTML (download resources)

Same, but may download the images and style sheets the page references.

HTML Source Formatted

The HTML source, formatted and cleaned by JSoup.

Document

Text extracted by Apache Tika from PDF, Office and other documents; needs the tika-app jar in lib/ and is limited by document.max_size (10 MB).

JSON

The response as a JSON tree.

XML

The response as an XML tree; nodes can be expanded or collapsed with a right-click.

The order of the renderers in the drop-down comes from view.results.tree.renderers_order. Custom renderers can be added by implementing org.apache.jmeter.visualizers.ResultRenderer and putting the jar in lib/ext.

Search: the Search check box opens a search field, with Case sensitive and Regular exp. options; matches are highlighted in the displayed text. It searches the visible text, so results differ between the Text and HTML views, and its regular expressions use the Java engine. Scroll automatically? keeps the newest sample selected.

The properties that keep the tree usable:

Property Default Effect

view.results.tree.max_results

500

Number of samples kept in the tree (since 3.2); 0 keeps all, which may use a lot of memory.

view.results.tree.max_size

10485760 (10 MB)

Largest response displayed; 0 disables the check. The component reference still mentions a 200 KB limit; 5.6.3 uses 10 MB.

view.results.tree.max_line_size

110000

Very long lines are broken to keep the UI responsive.

view.results.tree.soft_wrap_line_size

100000

Where the line breaking starts.

view.results.tree.simple_view_limit

10000

Above this size the text view switches to a faster, simpler mode; -1 disables it.

view.results.tree.renderers_order

core renderers

Order of the renderers in the drop-down.

jmeter.gui.refresh_period

500 ms

How often GUI listeners refresh.

A typical debugging loop for Bookshelf: run one thread for one iteration (threads=1), select POST /api/auth/login, check in Sampler result that the response code is 200, open Response data with the JSON Path Tester and try $.token to confirm the extractor expression, then disable the listener before any real run. The View Results Tree shows the token and the login body in full, which is another reason never to save its content to a shared file.

View Results in Table (Add → Listener → View Results in Table) shows one row per sample with Sample #, Start Time, Thread Name, Label, Sample Time(ms), Status, Bytes, Sent Bytes, Latency and Connect Time(ms). It shows parent samples only unless Child samples? is selected. It also keeps every row and is for debugging only.

Summary Report versus Aggregate Report

Both listeners create one row per distinct label, plus a TOTAL row, and compute throughput the same way. The difference is what they keep: the Summary Report keeps running totals only (constant memory), the Aggregate Report keeps the distribution needed for the median and percentiles.

Column Summary Aggregate Meaning

Label

Yes

Yes

The sample label; with Include group name in label? the Thread Group name is prefixed, so identical labels from different groups are reported separately.

# Samples

Yes

Yes

Number of samples with that label.

Average

Yes

Yes

Average elapsed time (ms).

Median

Yes

50th percentile: half the samples took no more than this.

90% Line

Yes

90 % of samples took no more than this (aggregate_rpt_pct1, default 90).

95% Line

Yes

95th percentile (aggregate_rpt_pct2, default 95).

99% Line

Yes

99th percentile (aggregate_rpt_pct3, default 99).

Min

Yes

Yes

Shortest elapsed time.

Max

Yes

Yes

Longest elapsed time.

Std. Dev.

Yes

Population standard deviation of the elapsed time (like STDEVP in spreadsheets).

Error %

Yes

Yes

Percentage of failed samples.

Throughput

Yes

Yes

Requests per second, minute or hour, with the unit chosen so that the value is at least 1.0; saved to CSV in requests per second.

Received KB/sec

Yes

Yes

Received kilobytes per second.

Sent KB/sec

Yes

Yes

Sent kilobytes per second.

Avg. Bytes

Yes

Average response size in bytes.

Both panels also have Save Table Data (writes the table to a CSV file) with Save Table Header, and Include group name in label?. In the .jmx these are stored as useGroupName (default false) and saveHeaders (default true). The three percentiles are set with properties, since 2.12:

# user.properties: report the 50th, 95th and 99.9th percentiles in the Aggregate Report and Aggregate Graph
aggregate_rpt_pct1=50
aggregate_rpt_pct2=95
aggregate_rpt_pct3=99.9

How throughput is computed. Throughput = number of samples ÷ (end of the last sample − start of the first sample), which includes any pauses between samples, because it is meant to represent the load on the server. Two consequences from the component reference:

  • timers and other samplers in the same thread lengthen the total time and so lower the throughput of each label;

  • two identical samplers with different names have half the throughput each of one sampler with a shared name, so choose labels deliberately.

Worked example. In the Bookshelf load run, GET /api/books has 5,616 samples between the first start at 10:00:00.000 and the last end at 10:11:00.000, 660 s later: throughput = 5,616 ÷ 660 ≈ 8.5/s. With an average of 5,320 bytes received per sample, the received rate is about 45,000 bytes per second, displayed as roughly 44 KB/sec. If the same endpoint had been split into two samplers named GET /api/books (search) and GET /api/books (browse) with 2,808 samples each, each row would show about 4.25/s.

Which one to use. Use the Summary Report when a quick table in the GUI is enough or in a debugging run. Use the Aggregate Report for percentiles, preferably by loading the JTL after the run, which the component reference names as the recommended way to avoid performance impacts. Aggregate Report memory grows with the number of distinct elapsed times, so long responses (several seconds, rarely identical) need more memory. For a report to share, the HTML dashboard is the better tool.

Aggregate Graph

Add → Listener → Aggregate Graph. The same statistics as the Aggregate Report, with a bar chart that can be saved as a PNG. It has a Settings tab and a Graph tab; after changing a filter, click Display Graph (and Apply filter) to refresh. None of these settings is saved in the .jmx.

Setting Meaning Required

Column settings

Columns to display (choose which statistics are drawn as bars); the colour of each column; Foreground color; Value font; Draw outlines bar?; Show number grouping?; Value labels vertical?; Column label selection, a filter on labels (regular expression allowed, for example .*TX.*) applied with Apply filter.

Yes

Title

Graph title; empty means "Aggregate Graph". Synchronize with name copies the listener name; font settings for the title.

No

Graph size

Dynamic size from the window, or Width and Height in pixels.

No

X Axis

Max length of x-axis label in pixels.

No

Y Axis

Scale maximum value.

No

Legend

Placement (Top, Bottom, Left, Right) and font.

Yes

Buttons: Save Graph (PNG) and Save Table Data. Like the Aggregate Report, it aggregates samples with identical elapsed times to save memory.

Response Time Graph

Add → Listener → Response Time Graph. A line chart of response time over the test, one line per label; samples are grouped into intervals and when several samples fall into the same timestamp the mean is drawn. Unlike the Aggregate Graph, its settings are saved in the .jmx file (since 2.12).

Setting Meaning Required

Interval (ms)

Width of the time buckets on the X axis; click Apply interval to recompute.

Yes

Sampler label selection

Regular-expression filter on labels, applied with Apply filter.

No

Title

Graph title; empty means "Response Time Graph"; Synchronize with name; title font.

No

Line settings

Stroke width and Shape point (Circle, Diamond, Square, Triangle or None).

Yes

Graph size

Dynamic, or Width and Height in pixels.

No

X Axis

Time format (SimpleDateFormat) for the axis labels.

No

Y Axis

Scale maximum value in ms, Increment scale and Show number grouping?.

No

Legend

Placement and font.

Yes

For the Bookshelf run, load the JTL afterwards with an interval of 10000 ms and the filter GET /api/books.* to see whether search times drift during the 10-minute hold. The HTML dashboard’s "Response Times Over Time" graph gives the same view without the GUI.

Simple Data Writer

Add → Listener → Simple Data Writer. It records results to a file and displays nothing, which removes the GUI overhead. Its only fields are the common file panel; the fields written come from its Configure dialog, initialised from the properties. In CLI mode, -l does the same job for the whole plan, so a Simple Data Writer is useful for a second file with a different scope or configuration, such as the errors-only XML file above or a file for one Thread Group only.

<ResultCollector guiclass="SimpleDataWriter" testclass="ResultCollector" testname="Borrowers only" enabled="true">
  <boolProp name="ResultCollector.error_logging">false</boolProp>
  <objProp>
    <name>saveConfig</name>
    <value class="SampleSaveConfiguration">
      <time>true</time>
      <latency>true</latency>
      <timestamp>true</timestamp>
      <success>true</success>
      <label>true</label>
      <code>true</code>
      <message>true</message>
      <threadName>true</threadName>
      <dataType>true</dataType>
      <encoding>false</encoding>
      <assertions>true</assertions>
      <subresults>true</subresults>
      <responseData>false</responseData>
      <samplerData>false</samplerData>
      <xml>false</xml>
      <fieldNames>true</fieldNames>
      <responseHeaders>false</responseHeaders>
      <requestHeaders>false</requestHeaders>
      <responseDataOnError>false</responseDataOnError>
      <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
      <assertionsResultsToSave>0</assertionsResultsToSave>
      <bytes>true</bytes>
      <sentBytes>true</sentBytes>
      <url>true</url>
      <threadCounts>true</threadCounts>
      <idleTime>true</idleTime>
      <connectTime>true</connectTime>
    </value>
  </objProp>
  <stringProp name="filename">~/results/borrowers.csv</stringProp>
</ResultCollector>
<hashTree/>

Placed as a child of the Borrowers Thread Group, it writes only that group’s samples.

Generate Summary Results

Add → Listener → Generate Summary Results. It periodically writes a summary of the run so far to the log and to standard output: a delta line (+) for the last interval and a running total line (=). Output is aligned to the interval boundary, so the first and last deltas are usually shorter; a line is written only if samples arrived in the interval. Its only field is Name, which is the label of the output: all summarisers with the same name add their samples together.

In CLI mode a summariser is created automatically, named by summariser.name (default summary). That is where the progress lines of a CLI run come from. The component reference warns that if you add a Generate Summary Results named summary to the plan as well, its samples are accumulated with the default summariser’s under that label and the totals become wrong; give your own a different name.

Property Default Effect

summariser.name

summary

Name of the automatic CLI summariser; comment it out to disable it.

summariser.interval

30

Seconds between summaries.

summariser.log

true

Write to jmeter.log.

summariser.out

true

Write to standard output.

summariser.ignore_transaction_controller_sample_result

true

Ignore Transaction Controller samples (since 3.1), so counts are sampler counts.

A Bookshelf load run (50 threads, 60 s ramp-up, 660 s duration, 3 s think time, about 0.2 s per request, so about 50 ÷ 3.2 ≈ 15.6 requests per second at full load) prints:

$ jmeter -n -t bookshelf-load.jmx -l results/load.jtl -Jthreads=50 -Jrampup=60 -Jduration=660
Creating summariser <summary>
Created the tree successfully using bookshelf-load.jmx
Starting standalone test @ 2026 Oct 14 10:00:00 UTC (1791972000000)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary +    117 in 00:00:30 =    3.9/s Avg:   196 Min:    38 Max:   612 Err:     0 (0.00%) Active: 25 Started: 25 Finished: 0
summary +    351 in 00:00:30 =   11.7/s Avg:   201 Min:    35 Max:   780 Err:     0 (0.00%) Active: 50 Started: 50 Finished: 0
summary =    468 in 00:01:00 =    7.8/s Avg:   200 Min:    35 Max:   780 Err:     0 (0.00%)
summary +    469 in 00:00:30 =   15.6/s Avg:   204 Min:    36 Max:   905 Err:     1 (0.21%) Active: 50 Started: 50 Finished: 0
summary =    937 in 00:01:30 =   10.4/s Avg:   202 Min:    35 Max:   905 Err:     1 (0.11%)
...
summary =   9844 in 00:11:00 =   14.9/s Avg:   205 Min:    35 Max:  2310 Err:    12 (0.12%)
Tidying up ...    @ 2026 Oct 14 10:11:01 UTC (1791972661000)
... end of run

How to read them: each thread does about 1 ÷ 3.2 ≈ 0.3125 requests per second. In the first 30 s the ramp-up grows from 0 to 25 active threads, 12.5 on average, so 12.5 × 0.3125 ≈ 3.9/s; in the next 30 s the average is 37.5 threads, about 11.7/s; after the ramp-up, 50 threads give the expected 15.6/s; the final total over 660 s is lower than 15.6/s because it includes the ramp-up. The Active, Started and Finished columns appear on delta lines only. These lines are the first place to watch during a run: a rising Avg at constant Active or a non-zero Err is a reason to look closer.

Save Responses to a file

Add → Listener → Save Responses to a file. For each sample in its scope it writes the response data to a file. Its main use is functional testing, or keeping responses too large for the View Results Tree; in a load test, use it only with Save Failed Responses only. It can be placed anywhere in the plan. (The user manual’s "Saving response data" section still calls it a post-processor; in 5.6.3 it is in the Listener menu.)

Field Meaning Required JMX property

Name

Descriptive name.

No

testname attribute

Filename Prefix (can include folders)

Prefix of the generated names, optionally with folders, relative or ~/ paths as for result files. Missing folders are created; if that fails the test stops. Must not contain thread-dependent data: no variables and no functions such as ${__threadNum}.

Yes

FileSaver.filename

Variable Name containing saved file name

Variable that receives the generated name; sub-samples get NAME1, NAME2…​

No

FileSaver.variablename

Minimum Length of sequence number

Zero-padding of the sequence number, default 0.

No

FileSaver.numberPadLen

Save Failed Responses only

Only failed samples are saved.

Yes

FileSaver.errorsonly

Save Successful Responses only

Only successful samples are saved.

Yes

FileSaver.successonly

Don’t add number to prefix

No sequence number; make sure the prefix is unique or files are overwritten.

Yes

FileSaver.skipautonumber

Don’t add content type suffix

No extension; same warning.

Yes

FileSaver.skipsuffix

Add timestamp

Adds the date in the format yyyyMMdd-HHmm_ after the prefix.

Yes

FileSaver.addTimstamp (spelled this way in the source)

Don’t Save Transaction Controller SampleResult

Ignore Transaction Controller samples.

Yes

FileSaver.ignoreTC

The file name is prefix + timestamp (if selected) + sequence number + . + the subtype of the content type (json for application/json, unknown if there is none). The sequence number is shared by all threads and restarts at each test start. The parent sample is saved first, then its sub-samples. The generated name is stored in the sample result, so Save Response Filename in a results file records which file holds each body, and a listener that loads that results file can show the saved body again.

<ResultSaver guiclass="ResultSaverGui" testclass="ResultSaver" testname="Save failed login bodies" enabled="true">
  <stringProp name="FileSaver.filename">~/results/failed/login-</stringProp>
  <stringProp name="FileSaver.variablename"></stringProp>
  <boolProp name="FileSaver.errorsonly">true</boolProp>
  <boolProp name="FileSaver.successonly">false</boolProp>
  <boolProp name="FileSaver.skipautonumber">false</boolProp>
  <boolProp name="FileSaver.skipsuffix">false</boolProp>
  <boolProp name="FileSaver.addTimstamp">true</boolProp>
  <stringProp name="FileSaver.numberPadLen">4</stringProp>
  <boolProp name="FileSaver.ignoreTC">true</boolProp>
</ResultSaver>
<hashTree/>

As the child of POST /api/auth/login, the first failed login of a run that started at 10:00 produces results/failed/login-20261014-1000_0001.json next to the .jmx.

JSR223 Listener

Add → Listener → JSR223 Listener. Runs a script for every sample result in its scope. It replaces the BeanShell Listener, whose documentation itself recommends migrating to JSR223 with Groovy.

Field Meaning Required JMX property

Name

Descriptive name; available to the script as Label.

No

testname attribute

Language

The JSR223 language; use groovy.

Yes

scriptLanguage

Parameters

Text passed to the script as Parameters (one string) and args (split on white space).

No

parameters

Script file

A script file; a relative path is resolved against the user.dir system property. Available as FileName.

No

filename

Script compilation caching

Cache the compiled script (Groovy supports it). Keep it selected, and do not put ${...} references in a cached script.

No

cacheKey

Script

The script, used when no file is given. The return value is ignored.

Yes, unless a file is given

script

Bindings available to the script: log, Label, FileName, Parameters, args, ctx, vars, props, sampleResult and prev (the result being processed), sampleEvent, sampler and OUT (System.out).

The script runs in the thread of the virtual user that produced the sample, for every sample, after the sample is complete. Its time is therefore not part of elapsed, but it delays that user’s next request and so lowers throughput: keep it short, avoid I/O, and do not write to a shared file from many threads. The example logs slow Bookshelf searches with their timing breakdown, reading the threshold from the search_sla_ms run property:

// JSR223 Listener (Groovy), child of the Thread Group "Bookshelf users"
long sla = (props.getProperty('search_sla_ms') ?: '600') as long
def r = sampleResult
if (r.sampleLabel.startsWith('GET /api/books') && r.time > sla) {
    log.warn('SLOW ' + r.sampleLabel
            + ' elapsed=' + r.time
            + ' latency=' + r.latency
            + ' connect=' + r.connectTime
            + ' idle=' + r.idleTime
            + ' code=' + r.responseCode
            + ' thread=' + r.threadName)
}
<JSR223Listener guiclass="TestBeanGUI" testclass="JSR223Listener" testname="Log slow searches" enabled="true">
  <stringProp name="scriptLanguage">groovy</stringProp>
  <stringProp name="parameters"></stringProp>
  <stringProp name="filename">scripts/log-slow-searches.groovy</stringProp>
  <stringProp name="cacheKey">true</stringProp>
  <stringProp name="script"></stringProp>
</JSR223Listener>
<hashTree/>

With -Jsearch_sla_ms=600, jmeter.log gets lines such as WARN ... SLOW GET /api/books elapsed=742 latency=731 connect=0 idle=0 code=200 thread=Bookshelf users 1-33, which show at a glance that the time was spent waiting for the server (latency close to elapsed) and not on the connection. Scripting in general is on Scripting with JSR223 & Groovy.

The other core listeners

  • Graph Results plots every sample time with the average, median, deviation and throughput. The documentation says it must not be used during a load test.

  • Assertion Results lists the label of each sample and the failures of its assertions; debugging only.

  • Mailer Visualizer sends an e-mail when the number of failures exceeds a limit (and a success e-mail after recovering), through an SMTP server configured in its fields.

  • BeanShell Listener is the legacy scripting listener; migrate to the JSR223 Listener.

  • Comparison Assertion Visualizer shows the results of Compare Assertions, which are not meant for load tests.

  • Backend Listener sends metrics asynchronously to Graphite or InfluxDB; it is the subject of the real-time monitoring page in this section.

Loading a JTL file into a listener after the run

The recommended workflow is: run in CLI mode with -l, then open the file in the GUI to look at it. Any listener can read a results file, CSV or XML.

  1. Start JMeter in GUI mode and open the plan, or create an empty Test Plan: the listener does not need the samplers.

  2. Add → Listener → Aggregate Report (or Summary Report, Response Time Graph, View Results Tree…​).

  3. Click Browse…​ in the file panel and select results/load.jtl. The file is read immediately and the table or graph is filled.

  4. To restrict the view, select Errors or Successes before loading.

  5. To load another file into the same listener, first clear it with Run → Clear (Ctrl+Shift+E) or Run → Clear All (Ctrl+E). JMeter does not clear existing data before loading, which also means two files loaded in sequence are merged, a simple way to combine the results of distributed load generators.

  6. To keep a picture of a view, use Edit → Save Node As Image; tables can be copied with the operating system’s copy shortcut and pasted into a spreadsheet.

When reading a CSV file, JMeter uses its header line to know which columns are present. A file without a header can only be read if the properties in effect match those used when the file was written. When loading a CSV file into the View Results Tree, there are no bodies to show (CSV has none) unless the file contains a Filename column pointing to files written by Save Responses to a file.

Loading into a GUI listener costs the same memory as running with it: a View Results Tree will keep at most view.results.tree.max_results samples, and an Aggregate Report on a multi-million-line file needs a large heap. For large files, generate the HTML dashboard instead, which works offline from the same JTL; see HTML Dashboard Report.

For the Bookshelf plan, the listener set looks like this.

Element State in load runs Why

View Results Tree (under the Test Plan)

Disabled

Used only with threads=1 while building the plan.

Summary Report

Disabled

Handy in the GUI; inert in CLI mode.

Simple Data Writer Errors with bodies (~/results/errors.xml, Errors only, XML with bodies)

Enabled

The bodies of failures, at a cost proportional to the error rate.

Save Responses to a file, child of the login request, failed only

Enabled

Raw bodies of failed logins, for comparison with server logs.

JSR223 Listener Log slow searches

Enabled

Cheap per-sample check that names slow searches in the log.

-l results/load.jtl with the user.properties above

Always

The CSV that feeds the dashboard and the analysis.

jmeter -n -t bookshelf-load.jmx -f -l results/load.jtl -j results/load.log \
  -Jthreads=50 -Jrampup=60 -Jduration=660 -Jsearch_sla_ms=600
ls results
# errors.xml  failed  load.jtl  load.log
head -1 results/load.jtl
# timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,Hostname,IdleTime,Connect,"term","bookId"

The results folder sits next to the .jmx here because the plan’s file names start with ~/ and the run is started from the plan’s directory with a relative -l path.

Pitfalls

  • Leaving the View Results Tree enabled with a file name. In CLI mode it does not display anything, but it still writes its file, possibly in XML and possibly with response data.

  • Appending to an old file. Two runs in one JTL produce a meaningless report. Use a new file per run or -f.

  • Comparing elapsed across runs with different idle-time settings. A Transaction Controller row changes by the whole think time depending on its Include duration of timer…​ option.

  • Reading latency on transaction rows. For a Transaction Controller, Latency and Connect are sums over the children.

  • Counting lines as requests. Since 5.0 sub-results (redirects, embedded resources, parent-mode transactions) are extra lines in the CSV.

  • Mixing up the start and end timestamp. With the shipped properties timeStamp is the start; scripts that assume the end are off by one response time.

  • Expecting failureMessage for HTTP errors. It is filled only by assertions.

  • Saving secrets. Request headers, sampler data and sample_variables can put the JWT and the login body into files.

  • Header-less CSV. Turning off print_field_names makes the file unreadable unless the exact same properties are set when it is read.

  • Two summarisers named summary. Totals are added together and the numbers are wrong.

  • Strict XML parsers. XML JTL files may not parse with strict XML 1.0 parsers.

  • Percentiles from the Aggregate Report during a run. It keeps the distribution in memory; load the file afterwards.

What changed: the books, 5.6 and 6.0

Area JMeter 2.11 to 3.0 (books) JMeter 5.6.3 (and planned 6.0)

Reporting

GUI listeners and jmeter-plugins.org graphs are the main reporting tool (Cookbook ch. 7).

The HTML dashboard (3.0 onwards) is the reporting tool; GUI listeners are for debugging or for reading a JTL afterwards.

Default columns

connect_time was added in 2.13 but off by default; no sentBytes; idle_time, print_field_names and assertion_results_failure_message became true in 3.0.

sentBytes (3.1), Connect on by default (3.1), URL on by default; header line and failureMessage by default.

Sub-results

Parent samples only in CSV.

Sub-results written to CSV and renamed with -N suffixes (5.0).

Removed listeners

Monitor Results, Distribution Graph, Spline Visualizer available.

Distribution Graph and Spline Visualizer removed in 3.1; Monitor Results removed in 3.2.

View Results Tree

Unlimited entries.

Capped at 500 by default (3.2); JSON JMESPath and Boundary Extractor testers; Save Responses to a file reworked (5.0).

Summariser

Enabled by default in non-GUI mode since 2.11.

Same, and ignores Transaction Controller samples by default (3.1).

Planned for 6.0 (per changes.xml on master)

Not applicable.

A space after : in the View Results Tree Sampler result tab, and support for JDK 25 and later in result collectors with empty file names. No change to the JTL format or to the jmeter.save.saveservice.* properties is listed. Re-check when 6.0 is released.

References