
Playwright Java API Testing | Creating custom logger for logging response details

While working on the series of tutorial blogs for GET, POST, PUT, PATCH and DELETE requests for API Automation using Playwright Java. I noticed that there is no logging method exposed by Playwright to log the request as well as responses.
In Rest-assured framework we have the log().all() method available that is used for logging the request as well as response, however, Playwright does not expose any such method. But, Playwright offers a text() method in the APIResponse interface that could be well used to extract the response text.
Playwright currently does not have the feature to access the Request body and Request Headers while performing API Testing.
As it is important to know what all headers and body were passed in the request, it would be good to have this feature added.
The Issue is already raised on GitHub for this feature, let’s add an upvote to this issue so this feature gets implemented soon in the framework
In this blog, we will be learning to extract the response and create a custom logger to log the response when any API tests using Playwright Java are executed.
How to Log Response Details in Playwright Java?
Before we begin with actual coding and implementation, let’s discuss the dependencies, configuration and setup.
Getting Started
As we are working with Playwright Java using Maven, we would be using the Log4J2 Maven dependency for logging the response details. The dependency for Jackson Databind will also be used for parsing the JSON response.
org.apache.logging.log4j
log4j-api
${log4j-api-version}
org.apache.logging.log4j
log4j-core
${log4j-core-version}
com.fasterxml.jackson.core
jackson-databind
${jackson-databind-version}
As a best practice, the versions of these dependencies will be added in the properties block as it allows users to easily check and update the newer version of dependencies in the project.
UTF-8
2.24.1
2.24.1
2.18.0
The next step is to create a log4j2.xml file in the src/main/resources folder . This file stores the configuration for logs such as log level, where should the logs be printed — to the console or to the file, pattern of the log layout, etc.
The section contains the information related to log printing and its pattern format to print. The section contains the log level details and how it should be printed. There can be multiple blocks of in the file, each for different log levels, such as “info”, “debug”, “trace”, etc.
Implementing the Custom Logger
A new java class Logger is created to implement the methods for logging the response details.
public class Logger {
private final APIResponse response;
private final org.apache.logging.log4j.Logger log;
public Logger (final APIResponse response) {
this.response = response;
this.log = LogManager.getLogger (getClass ());
}
//...
}
This class has the APIResponse interface of Playwright and the Logger interface from Log4j globally declared. This is done to ensure that we don’t duplicate the declaration of these classes in the method and use it efficiently.
The constructor of the Logger class is used for creating objects of the implementing classes of these interfaces. The APIResponse interface is added as a parameter as we need the response object to be supplied to this class for logging the respective details.
The logResponseDetails() method implements the function to log all the response details.
public void logResponseDetails() {
this.log.info ("Logging Response Details.....");
this.log.info ("Response Headers: \n{}", this.response.headers ());
this.log.info ("Status Code: {}", this.response.status ());
if (this.response.text () != null && !this.response.text ()
.isEmpty () && !this.response.text ()
.isBlank ()) {
this.log.info ("Response Body: \n{}", prettyPrintJson (this.response.text ()));
}
this.log.info ("End of Logs!");
}
The first statement in the code is an informational statement that will print mentioning that the log generation has started for logging the response.
The actual printing of the logs begins from the second statement in the method. The response headers will be printed when the second statement is executed. Next, the response status code will be printed in the log.
The response details is returned by the respective method like post(), put(), get(), etc. of APIResponse interface. However, there are cases like DELETE request where ideally the response body is not sent.

The if() condition is added here to handle the case where there is no response body sent. This condition checks that the response returned by response.text() is not null, not empty and not blank, then only the response will be logged.
As the response returned is not pretty printed, meaning the JSON format is shown in String in multiple lines wrapped up. This makes the logs look untidy.
So, to print the response in pretty format, we have created a prettyPrintJson() method that consumes the response in String format and returns it in pretty format.
private String prettyPrintJson (final String text) {
String prettyPrintJson = "";
if (text != null && !text.isBlank() && !text.isEmpty()) {
try {
final ObjectMapper objectMapper = new ObjectMapper ();
final Object jsonObject = objectMapper.readValue (text, Object.class);
prettyPrintJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
return prettyPrintJson;
} catch (final JsonProcessingException e) {
this.log.error ("Error Printing Pretty Json : {}", e.getMessage ());
}
}
this.log.info ("No response body generated!");
return null;
}
This method accepts the String in parameter where the response object will be supplied. A new String prettyPrintJson is declared and initialized with a blank value.
Next, a check is performed using the if() condition that the text supplied is not null and it is not blank and it is not empty. If the condition is satisfied then the ObjectMapper class from the Jackson Databind dependency is instantiated.
Next, the text value of the response is read and it is converted and returned as the JSON pretty print format using the writerWithDefaultPrettyPrinter() and writeValueAsString() methods of the ObjectMapper class.

If the response is null, empty and blank, it will print the log info message “No response body generated!!” and will return null meaning nothing will be returned and the method will be exited.
How to use the Logger in the API Automation tests?
The Logger class needs to be instantiated and its respective methods need to be called in order to get the response details printed while the tests are executed.
We need to make sure that we don’t write duplicate code everywhere in the tests to get the response details logged. In order to handle that we would be using the BaseTest class and creating a new method logResponse(APIResponse response).
This method will accept the response as a parameter and the logResponseDetails() will be called after instantiating the Logger class.
public class BaseTest {
//...
protected void logResponse (final APIResponse response) {
final Logger logger = new Logger (response);
logger.logResponseDetails ();
}
}
As the BaseTest class is extended to all the Test classes, it becomes easier to call the methods directly in the test class.
The HappyPathTests class that we have been using for adding happy scenario tests for testing GET, POST, PUT, PATCH and DELETE requests already extends the BaseTest class.
Let’s print the response logs for the POST and GET API request test.
The testShouldCreateNewOrders() verifies that the new orders are created successfully. Let’s add the logResponse() method to this test and get the response printed in the logs.
public class HappyPathTests extends BaseTest{
@Test
public void testShouldCreateNewOrders() {
final int totalOrders = 4;
for (int i = 0; i < totalOrders; i++) {
this.orderList.add(getNewOrder());
}
final APIResponse response = this.request.post("/addOrder", RequestOptions.create()
.setData(this.orderList));
logResponse (response);
//...
// Assertion Statements...
}
}
The logResponse() method will be called after the POST request is sent. This will enable us to know what response was received before we start performing assertions.
The testShouldGetAllOrders() verifies the GET /getAllOrder API request. Let’s add the logResponse() method to this test and check the response logs getting printed.
public class HappyPathTests extends BaseTest{
@Test
public void testShouldGetAllOrders() {
final APIResponse response = this.request.get("/getAllOrders");
logResponse (response);
final JSONObject responseObject = new JSONObject(response.text());
final JSONArray ordersArray = responseObject.getJSONArray("orders");
assertEquals(response.status(), 200);
assertEquals(responseObject.get("message"), "Orders fetched successfully!");
assertEquals(this.orderList.get(0).getUserId(), ordersArray.getJSONObject(0).get("user_id"));
assertEquals(this.orderList.get(0).getProductId(), ordersArray.getJSONObject(0).get("product_id"));
assertEquals(this.orderList.get(0).getTotalAmt(), ordersArray.getJSONObject(0).get("total_amt"));
}
}
The logResponse() method is called after the GET request is sent and will print the logs in the console.
Test Execution
The tests will be executed in order where POST request will be executed first so new orders are created and then the GET request will be executed. It will be done using the testng-restfulecommerce-postandgetorder.xml file.
http://testng.org/testng-1.0.dtd">
On executing the above testng-restfulecommerce-postandgetorder.xml file the POST as well as GET API requests are executed and the response is printed in the console that can be seen in the screenshots below:

POST API Response logs:


GET API Response logs:


The response is printed correctly in the logs and can now help us in knowing the exact results of the test execution.
Summary
Adding a custom logger in the project can help in multiple ways. It will provide us with the details of the test data that was processed along with the final output giving us control over the tests. It also helps in debugging the issue for failed tests and finding a fix quickly for it.
If the response data is readily available we can quickly find the pattern of the issue and try for a quick fix.
As Playwright does not provide any method for logging the response details, we can add our own custom logger that can help us in fetching the required details.
Happy Testing!!