First steps in Monitoring Micronaut apps with Prometheus and Grafana

Monitoring

Example of Grafana dashboard

Metrics

Settings: Micronaut App 3.42, JDK 17, Kotlin 1.6, Gradle, JUnit, Micrometer-Prometheus
micronaut:
application:
name: metrics
metrics:
export:
prometheus:
enabled: true
descriptions: true
step: PT1M
enabled: true
endpoints:
prometheus:
sensitive: false
@Controller("/ping")
class PingController {
@Inject lateinit var meterRegistry: MeterRegistry
@Get("/{name}")
fun ping(@NotBlank name: String): String {
meterRegistry.counter("ping", "param", name).increment()
return "pong $name"
}
}
fun verifyPrometheusEndpointTest() {
// when:
val request: HttpRequest<Any> = HttpRequest.GET("/prometheus")
val rsp = client.toBlocking().exchange(
request, Argument.of(String::class.java)
)
val body: String = rsp.body() ?: ""
// then:
assertEquals(HttpStatus.OK, rsp.status)
assertTrue(body.isNotBlank())
assertTrue(body.contains("http"))
assertFalse(body.contains("ping"))

}
fun verifyPingEndpointTest() {
// when:
val request: HttpRequest<Any> = HttpRequest.GET("/ping/test")
val rsp = client.toBlocking().exchange(
request, Argument.of(String::class.java)
)
val body = rsp.body()
// then
assertEquals(HttpStatus.OK, rsp.status)
assertTrue(body == "pong $name")
}
fun verifyPrometheusCustomMetricTest() {
// when:
val request: HttpRequest<Any> = HttpRequest.GET("/prometheus")
val rsp = client.toBlocking().exchange(
request, Argument.of(String::class.java)
)
val body: String = rsp.body() ?: ""
// then:
assertTrue(body.isNotBlank())
assertTrue(body.contains("http"))
assertTrue(body.contains("ping"))
}
mnk.metrics.controller.PingControllerTest

✔ Metrics Endpoint Successful Test
✔ Prometheus Endpoint Successful Test
✔ Ping Endpoint Successful Test
✔ Custom Metrics Successful Test
✔ Prometheus Custom Metrics Successful Test

Visualize

  1. Expose metrics (Micrometer): we should provide the data in the required format to be pulled, just as we did in the previous section.
  2. Pull metrics (Prometheus): designed to operate on a pull model, periodically scraping metrics from application instances, based on service discovery, and working as a data source.
  3. Visualize metrics (Grafana): collecting data from different data sources, allowing us to create insightful dashboards that are updated periodically, and can trigger specific alerts as well.
How to set a local instance to test the whole system

Microservice

./gradlew run
curl
http://localhost:8080/ping/hello
curl
http://localhost:8080/ping/world
curl http://localhost:8080/metrics/pingResult:

{ "name": "ping",
"measurements": [{
"statistic": "COUNT",
"value": 2.0
}],
"availableTags": [{
"tag": "param",
"values": [
"world",
"hello" ]
}]
}
curl http://localhost:8080/prometheusResult:

(...)
# HELP rest_ping_total
# TYPE rest_ping_total counter
rest_ping_total{param="hello"} 1.0
rest_ping_total{param="world"} 1.0
(...)

Prometheus

brew install prometheus
vi /usr/local/etc/prometheus.yml

- job_name: "micronaut"
metrics_path: "/prometheus"
scrape_interval: 5s
static_configs:
- targets: ["127.0.0.1:8080"]
brew services start prometheus
open
http://localhost:9090/targets
After adding our service to the configuration file, it should appear here
Testing out our custom metric in Prometheus

Grafana

brew install grafana
brew services start grafana
open
http://localhost:3000
open http://localhost:3000/datasources/new
open http://localhost:3000/dashboard/new
  • Ping Calls (Heatmap): sum(increase(ping_total[1m]))
  • HTTP Server Success(Graph): sum(increase(http_server_requests_seconds_count{status=~”2..”, uri!=”/prometheus”, uri!=”/metrics”}[1m]))
  • Http Max Duration(Stat): max(http_server_requests_seconds_max{status!~”5..”})
Final result of our initial dashboard

Summary

  • Securitize our endpoints with Micronaut Security
  • Pick more metrics and add more panels/rows to our dashboard
  • Learn the Grafana query language and how to use it properly

--

--

--

Senior Engineering Manager at @getuberall . Childhood with puzzles, Lego, and MSX. PhD, Passionate Dev, and Proud Father

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

New Guide:👏Track your coverage statistics with SonarQube and Buddy!

The goto legacy in Java

Linux, the little monster under some people‘s bed

💪Deploying WordPress on Azure using Bicep

How misunderstanding a GROUP BY messed up our Metrics Dashboard

Self Reflection of EKS_Training

A Super Easy Python Script for Web Scraping that Anybody can use

I’m a founder and CEO of a global software company.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ruben Mondejar

Ruben Mondejar

Senior Engineering Manager at @getuberall . Childhood with puzzles, Lego, and MSX. PhD, Passionate Dev, and Proud Father

More from Medium

Backup and Restore using OADP in OpenShift Cluster

Strategies for setting up K8S cluster on Local Machine

Implementing Kong API gateway with gRPC on a Kubernetes cluster

Kubernetes Service & Endpoints