Skip to content

Commit 5852529

Browse files
authored
Add simple healthcheck (#24)
Fixes #23
1 parent 9861a5d commit 5852529

2 files changed

Lines changed: 102 additions & 1 deletion

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package org.jlab.epics2web.controller;
2+
3+
import jakarta.json.Json;
4+
import jakarta.json.JsonArrayBuilder;
5+
import jakarta.json.JsonObjectBuilder;
6+
import jakarta.servlet.ServletException;
7+
import jakarta.servlet.annotation.WebServlet;
8+
import jakarta.servlet.http.HttpServlet;
9+
import jakarta.servlet.http.HttpServletRequest;
10+
import jakarta.servlet.http.HttpServletResponse;
11+
import java.io.IOException;
12+
import java.io.PrintWriter;
13+
import java.time.Duration;
14+
import java.time.Instant;
15+
import java.util.Map;
16+
import java.util.logging.Level;
17+
import java.util.logging.Logger;
18+
import org.jlab.epics2web.Application;
19+
import org.jlab.epics2web.epics.ChannelManager;
20+
import org.jlab.epics2web.epics.ChannelMonitor;
21+
22+
/**
23+
* Controller for Healthcheck page. Return 200 OK, for healthy Return 503 Service Unavailable for
24+
* unhealthy (or ANYTHING non 200-299).
25+
*
26+
* @author slominskir
27+
*/
28+
@WebServlet(
29+
name = "Healthcheck",
30+
urlPatterns = {"/healthcheck"})
31+
public class Healthcheck extends HttpServlet {
32+
33+
private final ChannelManager channelManager = Application.channelManager;
34+
private static final Logger LOGGER = Logger.getLogger(Healthcheck.class.getName());
35+
36+
/**
37+
* Handles the HTTP <code>GET</code> method.
38+
*
39+
* @param request servlet request
40+
* @param response servlet response
41+
* @throws ServletException if a servlet-specific error occurs
42+
* @throws IOException if an I/O error occurs
43+
*/
44+
@Override
45+
protected void doGet(HttpServletRequest request, HttpServletResponse response)
46+
throws ServletException, IOException {
47+
48+
boolean healthy = true;
49+
50+
Map<String, ChannelMonitor> monitorMap = channelManager.getMonitorMap();
51+
52+
Instant now = Instant.now();
53+
54+
JsonArrayBuilder unhealthyChannelArray = Json.createArrayBuilder();
55+
56+
for (Map.Entry<String, ChannelMonitor> entry : monitorMap.entrySet()) {
57+
String pv = entry.getKey();
58+
ChannelMonitor monitor = entry.getValue();
59+
60+
// If never an update, then we assume PV doesn't exist. Might miss some cases. Better than
61+
// nothing health check!
62+
if (monitor.getLastTimestamp() != null) {
63+
Instant lastTimestamp = monitor.getLastTimestamp().toInstant();
64+
Duration duration = Duration.between(now, lastTimestamp);
65+
long differenceInSeconds = Math.abs(duration.toSeconds());
66+
67+
if (monitor.getState() != ChannelMonitor.MonitorState.CONNECTED
68+
&& (differenceInSeconds > 30)) {
69+
healthy = false;
70+
JsonObjectBuilder unhealthyChannel = Json.createObjectBuilder();
71+
unhealthyChannel.add("name", pv);
72+
unhealthyChannel.add(
73+
"disconnected_minutes", String.format("%.1f", differenceInSeconds / 60.0));
74+
unhealthyChannelArray.add(unhealthyChannel);
75+
}
76+
}
77+
}
78+
79+
response.setContentType("application/json");
80+
81+
PrintWriter pw = response.getWriter();
82+
83+
response.setStatus(HttpServletResponse.SC_OK);
84+
85+
if (!healthy) {
86+
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
87+
}
88+
89+
String jsonStr = unhealthyChannelArray.build().toString();
90+
91+
pw.write(jsonStr);
92+
93+
pw.flush();
94+
95+
boolean error = pw.checkError();
96+
97+
if (error) {
98+
LOGGER.log(Level.SEVERE, "PrintWriter Error");
99+
}
100+
}
101+
}

src/main/java/org/jlab/epics2web/epics/ChannelMonitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public class ChannelMonitor implements Closeable {
6262
private final ExecutorService callbackExecutor;
6363
private final String pv;
6464

65-
enum MonitorState {
65+
public enum MonitorState {
6666
CONNECTING,
6767
CONNECTED,
6868
DISCONNECTED;

0 commit comments

Comments
 (0)