HelloKoding

Practical coding guides

Java Application Health Check with Prometheus, Grafana, MySQL and Docker Compose

This tutorial walks you through the steps of creating a Java Application Health Check Example with Prometheus, Grafana, MySQL and Docker Compose.

What you’ll need

  • Your favorite IDE
  • JDK 8+
  • Docker CE 18+

Stack

  • Java
  • Prometheus
  • Grafana
  • MySQL
  • Docker Compose

Init project structure and dependencies

Project structure

├── src
│   └── main
│       └── java
│           └── com
│               └── hellokoding
│                   └── monitoring
│                       ├── Application.java
│                       ├── DbHealthCheck.java
│                       └── HealthCheckExporter.java
├── Dockerfile
├── docker-compose.yaml
├── pom.xml
└── prometheus.yml

Project dependencies

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hellokoding.monitoring</groupId>
    <artifactId>healthcheck-prometheus</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <io.prometheus.simpleclient.version>0.6.0</io.prometheus.simpleclient.version>
        <com.github.strengthened.version>1.0.0</com.github.strengthened.version>
        <org.eclipse.jetty.version>8.1.7.v20120910</org.eclipse.jetty.version>
        <mysql.version>5.1.47</mysql.version>
        <maven.complier.version>3.1</maven.complier.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.github.strengthened</groupId>
            <artifactId>prometheus-healthchecks</artifactId>
            <version>${com.github.strengthened.version}</version>
        </dependency>
        <dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient</artifactId>
            <version>${io.prometheus.simpleclient.version}</version>
        </dependency>
        <dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient_hotspot</artifactId>
            <version>${io.prometheus.simpleclient.version}</version>
        </dependency>
        <dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient_servlet</artifactId>
            <version>${io.prometheus.simpleclient.version}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>${org.eclipse.jetty.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.hellokoding.monitoring.Application</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Define and Export Health Check for Prometheus

Define Health Check

Extends Strengthened’s HealthCheck and implements MySQL Database connection status checking method

DbHealthCheck.java

package com.hellokoding.monitoring;

import com.github.strengthened.prometheus.healthchecks.HealthCheck;
import com.github.strengthened.prometheus.healthchecks.HealthStatus;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Logger;

class DbHealthCheck extends HealthCheck {
    private final Logger logger = Logger.getLogger(this.getClass().getName());

    @Override
    public HealthStatus check() {
        return checkDbConnection() ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;
    }

    private boolean checkDbConnection() {
        try (Connection connection = DriverManager.getConnection("jdbc:mysql://db:3306/test?useSSL=false", "root", "hellokoding")) {
            logger.info("Database connected!");
            return true;
        } catch (SQLException e) {
            logger.info("Can not connect the database!");
            return false;
        }
    }
}

Export Health Check for Prometheus

Start a simple HTTP Sever by using Jetty, listening connection on port 8080 and expose health check through /metrics end point.

HealthCheckExporter.java

package com.hellokoding.monitoring;

import com.github.strengthened.prometheus.healthchecks.HealthChecksCollector;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.MetricsServlet;
import io.prometheus.client.hotspot.DefaultExports;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class HealthCheckExporter {
    private final HealthChecksCollector healthChecksCollector;

    public HealthCheckExporter(HealthChecksCollector healthChecksCollector) {
        this.healthChecksCollector = healthChecksCollector;
    }

    public void export() throws Exception {
        CollectorRegistry.defaultRegistry.register(this.healthChecksCollector);

        Server server = new Server(8080);
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        server.setHandler(context);

        context.addServlet(new ServletHolder(new MetricsServlet()), "/metrics");
        DefaultExports.initialize();

        server.start();
        server.join();
    }
}

Glue things together

Create a HealthChecksCollector with metric name as hello_koding_health_check (to be use as a key to query on Prometheus and Grafana), add above health check definition to the collector and trigger export function.

Application.java

package com.hellokoding.monitoring;

import com.github.strengthened.prometheus.healthchecks.HealthChecksCollector;

public class Application {
    public static void main(String[] args) throws Exception {
        HealthChecksCollector healthchecksMetrics = HealthChecksCollector.Builder.of().setGaugeMetricName("hello_koding_health_check").build();
        healthchecksMetrics.addHealthCheck("database", new DbHealthCheck());

        new HealthCheckExporter(healthchecksMetrics).export();
    }
}

Prepare Dockerfile to build the Java application

Dockerfile

FROM maven:3.5-jdk-8

Prepare docker-compose.yaml to provision MySQL, Prometheus and Grafana

docker-compose.yaml

version: '3'
services:
  grafana:
    image: grafana/grafana:5.4.2
    ports:
    - "3000:3000"
    depends_on:
    - prom

  prom:
    image: prom/prometheus:v2.6.0
    volumes:
    - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command: "--config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus"
    ports:
    - 9090:9090
    depends_on:
    - app

  app:
    build: .
    volumes:
    - .:/app
    - ~/.m2:/root/.m2
    working_dir: /app
    command: bash -c "mvn clean package && java -jar target/healthcheck-prometheus-1.0-SNAPSHOT-jar-with-dependencies.jar"
    ports:
    - "8080:8080"
    depends_on:
    - db

  db:
    container_name: some-mysql
    image: mysql/mysql-server:5.7
    environment:
      MYSQL_DATABASE: test
      MYSQL_ROOT_PASSWORD: hellokoding
      MYSQL_ROOT_HOST: '%'
    ports:
    - "3306:3306"
    restart: always

Start your application and infrastructure via Docker Compose. Make sure your local Docker is running

docker-compose up

Access to localhost:8080/metrics, you should see something like this in the console

# HELP hello_koding_health_check Health check status results
# TYPE hello_koding_health_check gauge
hello_koding_health_check{system="database",} 1.0

Access to Prometheus console through localhost:9090

Prometheus Console

And finally access to Grafana through localhost:3000

Grafana

Source code

https://github.com/hellokoding/hellokoding-courses/tree/master/java-examples/java-app-healthcheck-prometheus

Follow HelloKoding