Skip to content

Commit

Permalink
Merge pull request #229 from sladkoff/feature/health-checks
Browse files Browse the repository at this point in the history
feat: health checks
  • Loading branch information
sladkoff authored Feb 29, 2024
2 parents 1051314 + 9f5f58e commit 4dd4b45
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 10 deletions.
28 changes: 28 additions & 0 deletions src/main/java/de/sldk/mc/HealthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package de.sldk.mc;

import de.sldk.mc.health.HealthChecks;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;

public class HealthController extends Handler.Abstract {

private final HealthChecks checks;

private HealthController(final HealthChecks checks) {
this.checks = checks;
}

public static Handler create(final HealthChecks checks) {
return new HealthController(checks);
}

@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
response.setStatus(checks.isHealthy() ? HttpStatus.OK_200 : HttpStatus.SERVICE_UNAVAILABLE_503);
callback.succeeded();
return true;
}
}
5 changes: 4 additions & 1 deletion src/main/java/de/sldk/mc/MetricsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ public class MetricsController extends Handler.Abstract {
private final MetricRegistry metricRegistry = MetricRegistry.getInstance();
private final PrometheusExporter exporter;

public MetricsController(PrometheusExporter exporter) {
private MetricsController(PrometheusExporter exporter) {
this.exporter = exporter;
}

public static Handler create(final PrometheusExporter exporter) {
return new MetricsController(exporter);
}

@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/de/sldk/mc/MetricsServer.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.sldk.mc;

import org.eclipse.jetty.http.pathmap.PathSpec;
import de.sldk.mc.health.HealthChecks;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.PathMappingsHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
Expand All @@ -12,20 +13,23 @@ public class MetricsServer {
private final String host;
private final int port;
private final PrometheusExporter prometheusExporter;
private final HealthChecks healthChecks;

private Server server;

public MetricsServer(String host, int port, PrometheusExporter prometheusExporter) {
public MetricsServer(String host, int port, PrometheusExporter prometheusExporter, HealthChecks healthChecks) {
this.host = host;
this.port = port;
this.prometheusExporter = prometheusExporter;
}
this.healthChecks = healthChecks;
}

public void start() throws Exception {
GzipHandler gzipHandler = new GzipHandler();

var pathMappings = new PathMappingsHandler();
pathMappings.addMapping(PathSpec.from("/metrics"), new MetricsController(prometheusExporter));
pathMappings.addMapping(PathSpec.from("/metrics"), MetricsController.create(prometheusExporter));
pathMappings.addMapping(PathSpec.from("/health"), HealthController.create(healthChecks));

gzipHandler.setHandler(pathMappings);

Expand Down
33 changes: 33 additions & 0 deletions src/main/java/de/sldk/mc/health/ConcurrentHealthChecks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.sldk.mc.health;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public final class ConcurrentHealthChecks implements HealthChecks {

private final Set<HealthCheck> checks;

private ConcurrentHealthChecks(final Set<HealthCheck> checks) {
this.checks = checks;
}

public static HealthChecks create() {
return new ConcurrentHealthChecks(ConcurrentHashMap.newKeySet());
}

@Override
public boolean isHealthy() {
for (final HealthCheck check : checks) if (!check.isHealthy()) return false;
return true;
}

@Override
public void add(final HealthCheck check) {
checks.add(check);
}

@Override
public void remove(final HealthCheck check) {
checks.remove(check);
}
}
64 changes: 64 additions & 0 deletions src/main/java/de/sldk/mc/health/HealthCheck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package de.sldk.mc.health;

/**
* Health check.
*/
public interface HealthCheck {

/**
* Checks if the current state is healthy.
*
* @return {@code true} if the state is healthy and {@code false} otherwise
*/
boolean isHealthy();

/**
* Creates a health compound check from the provided ones reporting healthy status if all the checks report it.
*
* @param checks merged health checks
* @return compound health check
*/
static HealthCheck allOf(final HealthCheck... checks) {
return new AllOf(checks);
}

/**
* Creates a compound health check from the provided ones reporting healthy status if any check reports it.
*
* @param checks merged health checks
* @return compound health check
*/
static HealthCheck anyOf(final HealthCheck... checks) {
return new AnyOf(checks);
}

final class AllOf implements HealthCheck {
private final HealthCheck[] checks;

private AllOf(final HealthCheck[] checks) {
this.checks = checks;
}

@Override
public boolean isHealthy() {
for (final HealthCheck check : checks) if (!check.isHealthy()) return false;

return true;
}
}

final class AnyOf implements HealthCheck {
private final HealthCheck[] checks;

private AnyOf(final HealthCheck[] checks) {
this.checks = checks;
}

@Override
public boolean isHealthy() {
for (final HealthCheck check : checks) if (check.isHealthy()) return true;

return false;
}
}
}
21 changes: 21 additions & 0 deletions src/main/java/de/sldk/mc/health/HealthChecks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.sldk.mc.health;

/**
* Dynamic compound health checks.
*/
public interface HealthChecks extends HealthCheck {

/**
* Adds the provided health check to this one.
*
* @param check added health check
*/
void add(HealthCheck check);

/**
* Removes the provided health check from this one.
*
* @param check removed health check
*/
void remove(HealthCheck check);
}
14 changes: 11 additions & 3 deletions src/main/kotlin/de/sldk/mc/PrometheusExporter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
package de.sldk.mc

import de.sldk.mc.config.PrometheusExporterConfig
import de.sldk.mc.health.ConcurrentHealthChecks
import de.sldk.mc.health.HealthChecks
import org.bukkit.plugin.ServicePriority
import org.bukkit.plugin.java.JavaPlugin
import java.util.logging.Level


class PrometheusExporter : JavaPlugin() {
private val config: PrometheusExporterConfig = PrometheusExporterConfig(this)
private var server: MetricsServer? = null
Expand All @@ -14,14 +18,18 @@ class PrometheusExporter : JavaPlugin() {
override fun onEnable() {
config.loadDefaultsAndSave()
config.enableConfiguredMetrics()
startMetricsServer()

val healthChecks = ConcurrentHealthChecks.create()
getServer().servicesManager.register(HealthChecks::class.java, healthChecks, this, ServicePriority.Normal)

startMetricsServer(healthChecks)
}

private fun startMetricsServer() {
private fun startMetricsServer(healthChecks: HealthChecks) {
val host = config[PrometheusExporterConfig.HOST]
val port = config[PrometheusExporterConfig.PORT]

server = MetricsServer(host, port, this)
server = MetricsServer(host, port, this, healthChecks)

try {
server?.start()
Expand Down
19 changes: 16 additions & 3 deletions src/test/java/de/sldk/mc/exporter/PrometheusExporterTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package de.sldk.mc.exporter;


import static org.assertj.core.api.Assertions.assertThat;

import de.sldk.mc.MetricsServer;
import de.sldk.mc.PrometheusExporter;
import de.sldk.mc.health.ConcurrentHealthChecks;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.exporter.common.TextFormat;
Expand All @@ -21,6 +20,8 @@
import java.io.IOException;
import java.net.ServerSocket;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(MockitoExtension.class)
public class PrometheusExporterTest {

Expand All @@ -34,7 +35,9 @@ public class PrometheusExporterTest {
void setup() throws Exception {
CollectorRegistry.defaultRegistry.clear();
metricsServerPort = getRandomFreePort();
metricsServer = new MetricsServer("localhost", metricsServerPort, exporterMock);
metricsServer = new MetricsServer(
"localhost", metricsServerPort, exporterMock, ConcurrentHealthChecks.create()
);
metricsServer.start();
}

Expand Down Expand Up @@ -83,4 +86,14 @@ void metrics_server_should_return_404_on_unknown_paths() {
.statusCode(HttpStatus.NOT_FOUND_404);
}

@Test
void metrics_server_should_return_200_on_health_check() {
String requestPath = URIUtil.newURI("http", "localhost", metricsServerPort, "/health", null);

RestAssured.when()
.get(requestPath)
.then()
.statusCode(HttpStatus.OK_200);
}

}

0 comments on commit 4dd4b45

Please sign in to comment.