Log Failed JMeter Responses to a File (Groovy)
A JSR223 PostProcessor in Groovy that writes each failed response (status, body, thread and time) to a text file, and why to switch it off under load.
Mark
Performance Testing Expert
When you require a deeper level of logging from Apache JMeter you can use the Groovy programming language and a JSR223 PostProcessor to write the full detail of every failed request to a file.
Technical Approach
The Groovy script below runs after each sample and logs every one that failed — HTTP 4xx and 5xx responses, connection errors that never got an HTTP status at all, and samples failed by an assertion. For each it records:
- Response code and message
- Response body content
- Thread name
- Sample label
- Response timestamp (converted to a readable date and time)
Sample Implementation
import org.apache.jmeter.services.FileServer
// Log every failed sample. isSuccessful() is false for HTTP 4xx/5xx, for
// connection errors ("Non HTTP response code: ..."), and for failed assertions.
if (!prev.isSuccessful()) {
def dateTime = new Date(prev.getTimeStamp()).format("yyyy-MM-dd HH:mm:ss")
// Build the whole entry first, so it is written in a single call
def entry = new StringBuilder()
entry << "=" * 80 << "\n"
entry << "Thread: ${prev.getThreadName()}\n"
entry << "Sample: ${prev.getSampleLabel()}\n"
entry << "Time: ${dateTime}\n"
entry << "Status: ${prev.getResponseCode()} - ${prev.getResponseMessage()}\n"
entry << "-" * 40 << "\n"
entry << "Response:\n${prev.getResponseDataAsString()}\n"
entry << "=" * 80 << "\n\n"
def outputFile = new File(FileServer.getFileServer().getBaseDir(), "error_responses.txt")
// Every virtual user writes to the same file. One lock shared across all
// threads (held in JMeter properties) stops two entries interleaving.
def lock = props.computeIfAbsent("errorResponsesLock", { new Object() })
synchronized (lock) {
outputFile.append(entry.toString(), "UTF-8")
}
}
Two details in that script matter more than they look:
- It checks
isSuccessful(), not the status code. Converting the response code to a number and comparing it with 400 throws on a connection failure, whose “code” is text such asNon HTTP response code: java.net.ConnectException— so the script would crash on exactly the errors you most want to see. - Writes are serialised. Without the lock, two threads failing at the same moment can interleave their output and leave you with entries you cannot read.
Important Caveat
Writing data to files can limit the throughput of your test, and the lock means failing threads queue behind each other to write. Where you intend to push through high workloads, this level of logging may become obstructive — and when a system under load starts failing, it fails a lot at once.
Consider the following when using file-based logging:
- Use it primarily for debugging and low-volume tests
- Disable or remove the logging for high-throughput performance tests
- Filter carefully to only log errors or specific conditions
- Consider using backend listeners for production-level monitoring
Usage
- Add a JSR223 PostProcessor to your HTTP Request sampler (or to the Thread Group, to cover every sampler in it)
- Select “groovy” as the scripting language
- Paste the script into the Script panel
- Run your test
The script will create an error_responses.txt file in your test plan directory containing detailed information about failed requests.
The No-Code Alternative
If you do not need a custom format, JMeter can save failed response bodies itself. Add these to user.properties:
jmeter.save.saveservice.output_format=xml
jmeter.save.saveservice.response_data.on_error=true
Response bodies are only written in the XML results format, which is why the first line is needed. The result is larger and slower to write than CSV, so the same caveat applies — use it for debugging runs, not the load test itself.
Further Reading
Tags: