Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for health-checking #113

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/main/java/de/sldk/mc/HealthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.sldk.mc;

import de.sldk.mc.health.HealthChecks;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

public class HealthController extends AbstractHandler {

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 void handle(final String target,
final Request baseRequest,
final HttpServletRequest request,
final HttpServletResponse response) {
if (!target.equals("/health")) return;

response.setStatus(checks.isHealthy() ? HttpStatus.OK_200 : HttpStatus.SERVICE_UNAVAILABLE_503);
baseRequest.setHandled(true);
}
}
19 changes: 11 additions & 8 deletions src/main/java/de/sldk/mc/MetricsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

Expand All @@ -18,18 +19,20 @@ public class MetricsController extends AbstractHandler {
private final MetricRegistry metricRegistry = MetricRegistry.getInstance();
private final PrometheusExporter exporter;

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

@Override
public void handle(String target, Request request, HttpServletRequest httpServletRequest,
HttpServletResponse response) throws IOException {
public static Handler create(final PrometheusExporter exporter) {
return new MetricsController(exporter);
}

if (!target.equals("/metrics")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
@Override
public void handle(final String target,
final Request request,
final HttpServletRequest httpServletRequest,
final HttpServletResponse response) throws IOException{
if (!target.equals("/metrics")) return;

/*
* Bukkit API calls have to be made from the main thread.
Expand Down
15 changes: 11 additions & 4 deletions src/main/java/de/sldk/mc/MetricsServer.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package de.sldk.mc;

import de.sldk.mc.health.HealthChecks;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;

import java.net.InetSocketAddress;
Expand All @@ -10,22 +12,27 @@ 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();
gzipHandler.setHandler(new MetricsController(prometheusExporter));
GzipHandler metricsHandler = new GzipHandler();
metricsHandler.setHandler(MetricsController.create(prometheusExporter));

InetSocketAddress address = new InetSocketAddress(host, port);
server = new Server(address);
server.setHandler(gzipHandler);
server.setHandler(new HandlerList(
metricsHandler,
HealthController.create(healthChecks)
));

server.start();
}
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/de/sldk/mc/PrometheusExporter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
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;
Expand All @@ -17,14 +20,17 @@ public void onEnable() {

config.enableConfiguredMetrics();

startMetricsServer();
HealthChecks healthChecks = ConcurrentHealthChecks.create();
getServer().getServicesManager().register(HealthChecks.class, healthChecks, this, ServicePriority.Normal);

startMetricsServer(healthChecks);
}

private void startMetricsServer() {
private void startMetricsServer(HealthChecks healthChecks) {
String host = config.get(PrometheusExporterConfig.HOST);
Integer port = config.get(PrometheusExporterConfig.PORT);

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

try {
server.start();
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 iff 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 iff 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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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 Down Expand Up @@ -43,7 +44,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