CTRLK

Integrate Prometheus with Infobip WhatsApp API

|

View as Markdown

This tutorial is a walk-through for creating a simple monitoring and alerting system using Prometheus and Infobip's WhatsApp API. You can use this example if you already have a Prometheus server running, and want to receive alerts on your phone via WhatsApp.

To simulate the existing monitoring system, we will create a local setup with a Prometheus server configured to scrape its own metrics and evaluate alerting rules based on them. Alerts will be sent to Alertmanager. A basic Spring Boot application will receive these alerts from Alertmanager and forward them to a mobile phone via Infobip's WhatsApp API.

Products and channels [#products-channels]

This tutorial uses the WhatsApp channel, but it can be adapted for other channels.

Prerequisites [#prerequisites]

  • Docker
  • Infobip account (if you do not have one, you can create it here)
  • Infobip API key with scope: whatsapp:message:send

Technical overview [#technical-specifications-diagram]

The system consists of three main components:

  • Prometheus - A monitoring system that collects metrics from various sources.
  • Alertmanager - A component that handles alerts sent by Prometheus.
  • Spring Boot application - Service that listens for alerts from Alertmanager and sends them to your phone over Infobip's WhatsApp API.

The diagram below provides a high-level overview of the system we will build.

Tutorials - Monitoring Diagram

Implementation [#implementation-steps]

Step 1: Starting the project [#starting-the-project-implementation-steps]

The code used in this tutorial is available on GitHub. You can clone it by running the following command:

shell
git clone [email protected]:infobip/prometheus-whatsapp-tutorial.git

To get the project up and running, you need to populate the .env file with your personal:

  • Infobip API key (with scope whatsapp:message:send)
  • Infobip base URL
  • Infobip sender phone number
  • Recipient phone number

Then, run the following command from the project's root directory:

shell
docker compose up --build -d

This setup will launch instances of Prometheus and Alertmanager, accessible at http://localhost:9090 and http://localhost:9093 respectively.

Additionally, a convenience UI will be available at http://localhost:8080/index.html for troubleshooting purposes. Within a few minutes, you should receive an alert on your phone.

To shut down the example, run the following command:

shell
docker compose down

Step 2: Understanding monitoring setup [#understanding-monitoring-setup-implementation-steps]

The Prometheus instance is configured to scrape its own metrics and evaluate an alerting rule that will trigger an alert after one minute.

The Alertmanager allows configuring a generic receiver of the alerts. In our case, it will be an endpoint exposed by the Spring boot application.

One minute after Prometheus starts, the alert should become active. You can check it by visiting http://localhost:9090 and selecting the Alerts tab.

Tutorials - Prometheus Dashboard

The same alert will also be triggered in Alertmanager. You can access it on http://localhost:9093

Tutorials - Alertmanager Dashboard

Step 3: Understanding Spring boot application [#understanding-spring-boot-application-implementation-steps]

The Spring boot application uses Infobip specific properties to define the API endpoint, API key, sender and receiver phone numbers.

properties
1infobip.whatsapp.base-url=https://${INFOBIP_BASE_URL}.api.infobip.com/whatsapp/1/message/text
2infobip.whatsapp.api-key=App ${INFOBIP_API_KEY}
3infobip.whatsapp.sender=${SENDER_NUMBER}
4infobip.whatsapp.receiver=${RECEIVER_NUMBER}

These properties are loaded in the properties class.

java
1@Component
2@ConfigurationProperties("infobip.whatsapp")
3class InfobipProperties {
4 
5 private String baseUrl;
6 private String apiKey;
7 private String sender;
8 private String receiver;
9 /// ...
10}

They are used in a WhatsApp client to send the message over Infobip's WhatsApp API.

java
1@Component
2class InfobipWhatsAppClient {
3 
4 private final Logger log = LoggerFactory.getLogger(InfobipWhatsAppClient.class);
5 private final HttpClient client = HttpClient.newHttpClient();
6 
7 private final String baseUrl;
8 private final String apiKey;
9 private final String sender;
10 private final String receiver;
11 
12 InfobipWhatsAppClient(InfobipProperties properties) {
13 this.baseUrl = properties.getBaseUrl();
14 this.apiKey = properties.getApiKey();
15 this.sender = properties.getSender();
16 this.receiver = properties.getReceiver();
17 }
18 
19 void sendMessage(String message) {
20 var request = HttpRequest.newBuilder()
21 .uri(URI.create(baseUrl))
22 .header("Authorization", apiKey)
23 .header("Content-Type", "application/json")
24 .header("Accept", "application/json")
25 .POST(HttpRequest.BodyPublishers.ofString(createPayload(message)))
26 .build();
27 
28 try {
29 var response = client.send(request, HttpResponse.BodyHandlers.ofString());
30 log.info("Response: {}", response.body());
31 } catch (IOException | InterruptedException e) {
32 log.error("Failed to send message", e);
33 }
34 }
35 
36 private String createPayload(String message) {
37 return """
38 {
39 "from": "%s",
40 "to": "%s",
41 "content": {
42 "text" : "%s"
43 }
44 }""".formatted(sender, receiver, message);
45 }
46}

The endpoint that receives alerts is defined in a listener.

java
1@RestController
2class AlertsListener {
3 
4 private final InfobipWhatsAppClient whatsAppClient;
5 
6 AlertsListener(InfobipWhatsAppClient whatsAppClient) {
7 this.whatsAppClient = whatsAppClient;
8 }
9 
10 @PostMapping("/send-alert")
11 void sendWhatsAppMessage(@RequestBody Alerts alerts) {
12 alerts.alerts()
13 .forEach(alert -> whatsAppClient.sendMessage(alert.text()));
14 }
15 
16}

This endpoint uses the Alerts record to extract the subset of information from the alert sent from Alertmanager.

java
1record Alerts(List<Alert> alerts) {
2 
3 record Alert(String status, Map<String, Object> labels, Map<String, Object> annotations) {
4 
5 String text() {
6 var severity = (String) labels.get("severity");
7 var instance = (String) labels.get("instance");
8 var summary = (String) annotations.get("summary");
9 return """
10 %s!
11 The alert for instance running on '%s' is %s
12 %s
13 """.formatted(severity, instance, status, summary).replace("\n", "\\n");
14 }
15 
16 }
17 
18}

The format of the alert sent by Alertmanager can be seen here.

After the application receives the alert, it will send it over Infobip's WhatsApp API. The response from Infobip API will be automatically rendered on the http://localhost:8080/index.html page.

Tutorials - Infobip Response

Shortly after the alert is sent, you should receive a message on your phone.

Tutorials - WhatsApp notification Tutorials - WhatsApp conversation

Troubleshooting

In case the message is not delivered, you can inspect the message status by logging in to your Infobip account and navigate to Analyze → Logs where you can use the messageId from the response to check the delivery status.

Next steps [#next-steps]

This example can be extended by looking into various options Infobip offers for sending WhatsApp messages and expanded by integrating other communication channels.