Supercharge Wiremock Logging: A Deep Dive into Custom Database Logging
Wiremock, a beloved tool for mocking HTTP APIs, offers robust functionality. But when it comes to logging, its default options might leave you wanting more. This blog post empowers you to craft a custom notifier that transcends these limitations, enabling you to store intricate request and response data directly within a database of your choice.
Why Custom Database Logging Reigns Supreme
- Unveiling the Depths of API Interactions: Gone are the days of rudimentary logs. With custom database logging, you can meticulously store even the most granular details of request and response objects. This empowers you to comprehensively analyze API behavior, identify patterns, and gain invaluable insights into how your APIs are being utilized.
- Fortifying Your Audit Trail: Regulatory compliance or internal governance might necessitate a centralized log for auditing purposes. A custom database notifier fulfills this need by meticulously recording every API interaction within your database. This provides a tamper-proof, auditable record that simplifies compliance and bolsters security.
- Streamlining Debugging Efforts: When API issues arise, debugging can become a tedious endeavor. Custom database logging offers a lifeline. By meticulously storing request and response data, you can reconstruct the exact sequence of events that led to the problem. This pinpoints the root cause with laser focus, saving you precious time and frustration.
Harnessing the Power of the Notifier Interface
Wiremock's built-in Notifier
interface acts as the cornerstone for extending logging capabilities. This interface outlines a set of methods that correspond to various events that occur during Wiremock's operation. By creating a class that implements this interface, we can craft a custom notifier that seamlessly integrates with Wiremock's logging framework.
Let's understand the default consolenotifier class:
WireMockServer wireMockServer = new WireMockServer(WireMockConfiguration.options().port(57167).usingFilesUnderDirectory("src/main/resources").notifier(new ConsoleNotifier(true)));
import com.github.tomakehurst.wiremock.WireMockServer;import com.github.tomakehurst.wiremock.core.WireMockConfiguration;import com.github.tomakehurst.wiremock.common.ConsoleNotifier;public class WireMockExample {public static void main(String[] args) {// Configure WireMock with ConsoleNotifierWireMockConfiguration wireMockConfig = WireMockConfiguration.options().notifier(new ConsoleNotifier(true)); // Enable verbose logging// Start WireMock serverWireMockServer wireMockServer = new WireMockServer(wireMockConfig);wireMockServer.start();// Your WireMock mappings and stubbing configurations go here// Stop WireMock serverwireMockServer.stop();}}
Crafting a Database Logging Powerhouse
Roadmap for constructing a DatabaseLoggingNotifier
class. Here's a closer look at the key steps involved:
-
Extending the
Notifier
Interface: This forms the foundation of our custom notifier. By inheriting the interface, we gain access to core logging functionalities offered by Wiremock. -
Establishing the Database Connection: To interact with the database, our notifier class needs the necessary credentials. This includes the JDBC URL, username, and password for the target database. These connection details are typically stored as variables within the class.
-
Building the
logRequestResponse
Method: This is the heart of our custom notifier. It takes center stage when Wiremock receives a request and generates a response. Here's what this method accomplishes: - Acquiring Request and Response Objects: The method receives the request and response objects as parameters.
- Constructing the SQL INSERT Statement: Leveraging the data extracted from the request and response objects, the method meticulously crafts an SQL INSERT statement. This statement is designed to insert the relevant data into a predefined table within your database.
- Storing Data in the Database: The notifier utilizes a JDBC Connector (e.g., MySQL Connector) to establish a connection to the database. Once connected, it executes the SQL INSERT statement, permanently etching the request and response data into the database table.
import com.github.tomakehurst.wiremock.common.Notifier;import com.github.tomakehurst.wiremock.http.Request;import com.github.tomakehurst.wiremock.http.Response;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.SQLException;import java.text.SimpleDateFormat;import java.util.Date;public class DatabaseLoggingNotifier implements Notifier {private final String jdbcUrl;private final String username;private final String password;private final SimpleDateFormat dateFormat;public DatabaseLoggingNotifier(String jdbcUrl, String username, String password) {this.jdbcUrl = jdbcUrl;this.username = username;this.password = password;this.dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");}@Overridepublic void info(String message) {// Ignore other types of messages}@Overridepublic void error(String message) {// Ignore other types of messages}@Overridepublic void error(String s, Throwable throwable) {}public void logRequestResponse(Request request, Response response) {try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {String timestamp = dateFormat.format(new Date());String insertQuery = "INSERT INTO request_response_log (timestamp, request_url, request_method, request_body, " +"response_status_code, response_body) VALUES (?, ?, ?, ?, ?, ?)";try (PreparedStatement preparedStatement = connection.prepareStatement(insertQuery)) {preparedStatement.setString(1, timestamp);preparedStatement.setString(2, request.getUrl());preparedStatement.setString(3, request.getMethod().toString());preparedStatement.setString(4, request.getBodyAsString());preparedStatement.setInt(5, response.getStatus());preparedStatement.setString(6, response.getBodyAsString());preparedStatement.executeUpdate();}} catch (SQLException e) {e.printStackTrace();}
}}
Database Structure
Here we are using MYSQL database. We can use any database for logging, just the compatible connector need to be used to create the connection. Below is the MYSQL database structure used in above example.
CREATE TABLE `request_response_log` (`id` int NOT NULL AUTO_INCREMENT,`timestamp` timestamp NULL DEFAULT NULL,`request_url` varchar(255) DEFAULT NULL,`request_method` varchar(10) DEFAULT NULL,`request_body` text,`response_status_code` int DEFAULT NULL,`response_body` text,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;Setting the Stage for Integration
Initializing the Database Logging Notifier:
- We begin by creating an instance of the
DatabaseLoggingNotifier
class. This class serves as the bridge between Wiremock and your database. - To establish a connection, you'll need to provide the JDBC URL, username, and password for your chosen database. We will be using a MySQL database on localhost.
- We begin by creating an instance of the
-
Instructing Wiremock to Use the Notifier:
- We need to explicitly tell it to leverage our custom notifier for logging purposes. This involves registering the notifier instance with the Wiremock server.
- The
WireServer.addMockServiceListener
method is used to register a listener that's notified whenever a request is received and a response is about to be generated.
public static void main(String[] args) {DatabaseLoggingNotifier notifier = new DatabaseLoggingNotifier("jdbc:mysql://localhost:3306/wiremock_logs?useSSL=false","root","Yourpassword");WireMockServer wireMockServer = new WireMockServer(WireMockConfiguration.options().port(57167).usingFilesUnderDirectory("src/main/resources").notifier(notifier));wireMockServer.addMockServiceRequestListener(notifier::logRequestResponse);//new ConsoleNotifier(true)wireMockServer.start();int port = wireMockServer.port();System.out.println(port);configureFor("localhost",57167);new V3stubs().pathregexstub();}
Understanding the Registration Process
-
Breaking Down
WireServer.addMockServiceListener
:- This method belongs to the
WireServer
class, the heart of Wiremock's standalone server functionality. - It allows you to register a custom listener that will be notified during crucial events in Wiremock's request-response cycle. These events include receiving a request, processing it against stubs, and preparing the response.
- This method belongs to the
-
The Power of Method References:
- When registering the listener, we provide a method reference to the
logRequestResponse
method within yourDatabaseLoggingNotifier
class. - This essentially tells Wiremock, "Whenever there's an interesting event, call this specific method on my custom notifier object."
- When registering the listener, we provide a method reference to the
The Magic Behind logRequestResponse
Retrieving Request and Response Objects:
- The
logRequestResponse
method receives the request and response objects as parameters. These objects encapsulate all the juicy details of the interaction, including URLs, methods, headers, bodies, and status codes.
- The
-
Constructing the SQL INSERT Statement:
- By leveraging the data extracted from the request and response objects, the method meticulously crafts an SQL INSERT statement. This statement is designed to insert relevant data into the predefined table within your database.
-
Storing Data in the Database:
- The notifier utilizes a JDBC Connector (e.g., MySQL Connector) to establish a connection to the database. Once connected, it executes the SQL INSERT statement, permanently etching the request and response data into the database table.
Witnessing the Integration in Action
Here are the key takeaways:
Verification through Testing:
- After integrating the custom notifier, it's crucial to test its functionality. Send GET and POST requests to predefined stubs and verifying that the corresponding request and response data are successfully logged in the database table.
- Ensure you're using the newly created database logging notifier instance when registering the listener with Wiremock. Otherwise, Wiremock might not use your custom logging logic.
Comments
Post a Comment