The app will start every minute a scheduled task to verify if a monitored API is healthy, i.e.:
2019-01-13 14:19:00,003 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:20:00,004 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:21:00,005 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:22:00,004 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:23:00,006 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:24:00,002 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:25:00,005 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:26:00,005 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:27:00,006 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
2019-01-13 14:28:00,006 INFO [scheduling-1] cristina.tech.rest.api.alert.manager.MonitorScheduledTask: Monitoring call for: Monitor(url=https://blox.weareblox.com/api/markets, method=GET, body=null, jsonPath=amount, alertText={ "text": "Prod alert: There is no markets pricing data: https://blox.weareblox.com/api/markets", "icon_emoji": ":fire:", "attachments": [ { "text": "Fix?", "color": "danger", "attachment_type": "alert", "actions": [ { "name": "fix", "text": "Redeploy Dataflow Runner", "type": "button" } ] } ] }, statusCode=200)
In the example above there is only one API monitor with its own alert message formatted based on Slack Message Builder
Demonstrated concepts:
- Scheduled task / crontab job with Spring
- Configure basic monitors and alerting channels in yaml (customizable external config)
- Load yaml as Java POJOs with Spring
- Send monitor API REST calls with Spring's RestTemplate
- Map String API call response as JsonNode with Jackson JSON
- Check if a monitored configurable JsonNode is present and has data (aka monitored API is healthy)
- If monitor API becomes unhealthy, post a message to a Slack channel using Spring's RestTemplate
Usage:
- Clone and build code and Docker image:
$ git clone https://github.com/cristinanegrean/api-data-monitor.git
$ cd api-data-monitor
$ ./gradlew clean build docker
- Create a
.env
externalized config file to store your alert channel web hook URL and authorization bearer token
$ cat /Users/cristinanegrean/api_data_monitor.env
WEB_HOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
AUTH_TOKEN="put here your htoken"
- Change env_file path in
docker-compose.yml
to point to location of file you created at step 2)
env_file:
- /Users/cristinanegrean/api_data_monitor.env
- Start-up application
$ docker-compose up
Deploy on Kubernetes:
- Mount the ConfigMaps as the volume inside the Spring Boot application Docker container
- Build Helm chart and load Spring Boot application.yaml via Kubernetes ConfigMaps
- Install Helm chart in Kubernetes cluster