57 private final List<ProbeCheck> probes;
61 this.probes = createProbes(dataSource);
71 return List.of(Route.of(
"/", Set.of(
"GET")));
76 final var jx = asJetty(x);
79 final var overallStatus = report.status();
81 final var checksMap =
new LinkedHashMap<String, Object>();
82 for (
final var result : report.results()) {
83 checksMap.put(result.name(), Map.of(
"status", result.status().name(),
"detail",
84 result.detail() !=
null ? result.detail() :
"",
"kind", result.kind().name()));
87 final var body =
new LinkedHashMap<String, Object>();
89 body.put(
"service",
"kiwi");
90 body.put(
"version",
"0.1.0-SNAPSHOT");
91 body.put(
"timestamp", Instant.now().toString());
93 body.put(
"status", overallStatus.name());
96 body.put(
"system", getSystemMetrics());
98 if (!checksMap.isEmpty()) {
99 body.put(
"checks", checksMap);
103 RESPONSES.json(jx.response(), jx.callback(), httpStatus, body);
109 return Set.of(
"GET");
112 private List<ProbeCheck> createProbes(
final DataSource dataSource) {
113 return List.of(dbProbe(dataSource), memoryProbe(), cpuProbe(), diskProbe(), applicationProbe());
116 private static ProbeCheck dbProbe(
final DataSource ds) {
118 try (var conn = ds.getConnection()) {
119 final var ok = conn.isValid(1);
120 return new ProbeResult(
"database", ProbeKind.READINESS, ok ? ProbeStatus.UP : ProbeStatus.DOWN,
121 ok ?
"connected" :
"validation failed");
122 }
catch (
final Exception e) {
123 return new ProbeResult(
"database", ProbeKind.READINESS, ProbeStatus.DOWN, e.getMessage());
128 private static ProbeCheck memoryProbe() {
130 final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
131 final var heapUsage = memoryBean.getHeapMemoryUsage();
132 final var maxHeap = heapUsage.getMax();
133 final var usedHeap = heapUsage.getUsed();
135 final double usagePercentage = maxHeap > 0 ? ((double) usedHeap / maxHeap) * 100 : 0;
137 final var status = usagePercentage > 90
139 : usagePercentage > 80 ? ProbeStatus.DEGRADED : ProbeStatus.UP;
141 return new ProbeResult(
"memory", ProbeKind.HEALTH, status, String.format(
"Heap: %.1f%% used (%,d/%,d MB)",
142 usagePercentage, usedHeap / (1024 * 1024), maxHeap / (1024 * 1024)));
146 private static ProbeCheck cpuProbe() {
148 final OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
149 if (osBean instanceof com.sun.management.OperatingSystemMXBean sunOsBean) {
150 final double systemLoad = sunOsBean.getSystemCpuLoad();
151 final double processLoad = sunOsBean.getProcessCpuLoad();
153 final var status = systemLoad > 0.9 ? ProbeStatus.DEGRADED : ProbeStatus.UP;
155 return new ProbeResult(
"cpu", ProbeKind.HEALTH, status,
156 String.format(
"System: %.1f%%, Process: %.1f%%", systemLoad * 100, processLoad * 100));
158 return new ProbeResult(
"cpu", ProbeKind.HEALTH, ProbeStatus.UP,
"CPU metrics not available");
162 private static ProbeCheck diskProbe() {
165 final var file =
new java.io.File(
".");
166 final var total = file.getTotalSpace();
167 final var free = file.getFreeSpace();
169 final double freePercentage = total > 0 ? ((double) free / total) * 100 : 0;
171 final var status = freePercentage < 10
173 : freePercentage < 20 ? ProbeStatus.DEGRADED : ProbeStatus.UP;
175 return new ProbeResult(
"disk", ProbeKind.HEALTH, status, String.format(
"Free: %.1f%% (%,d/%,d MB)",
176 freePercentage, free / (1024 * 1024), total / (1024 * 1024)));
177 }
catch (
final Exception e) {
178 return new ProbeResult(
"disk", ProbeKind.HEALTH, ProbeStatus.DOWN, e.getMessage());
183 private static ProbeCheck applicationProbe() {
185 final var threadCount = Thread.activeCount();
186 final var uptime = ManagementFactory.getRuntimeMXBean().getUptime();
188 return new ProbeResult(
"application", ProbeKind.HEALTH, ProbeStatus.UP,
189 String.format(
"Threads: %d, Uptime: %d minutes", threadCount, uptime / (60 * 1000)));
193 private Map<String, Object> getSystemMetrics() {
194 final var metrics =
new LinkedHashMap<String, Object>();
197 final var runtime = Runtime.getRuntime();
198 final var memoryBean = ManagementFactory.getMemoryMXBean();
199 final var heapUsage = memoryBean.getHeapMemoryUsage();
200 final var nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
201 final var osBean = ManagementFactory.getOperatingSystemMXBean();
202 final var runtimeBean = ManagementFactory.getRuntimeMXBean();
204 metrics.put(
"jvm", Map.of(
"name", runtimeBean.getVmName(),
"version", runtimeBean.getVmVersion(),
"vendor",
205 runtimeBean.getVmVendor(),
"uptime", runtimeBean.getUptime()));
207 metrics.put(
"memory", Map.of(
"heap", Map
208 .of(
"used", heapUsage.getUsed(),
"committed", heapUsage.getCommitted(),
"max", heapUsage.getMax()),
210 Map.of(
"used", nonHeapUsage.getUsed(),
"committed", nonHeapUsage.getCommitted(),
"max",
211 nonHeapUsage.getMax()),
212 "availableProcessors", runtime.availableProcessors(),
"totalMemory", runtime.totalMemory(),
213 "freeMemory", runtime.freeMemory(),
"maxMemory", runtime.maxMemory()));
216 Map.of(
"name", osBean.getName(),
"version", osBean.getVersion(),
"architecture", osBean.getArch(),
217 "availableProcessors", osBean.getAvailableProcessors(),
"systemLoadAverage",
218 osBean.getSystemLoadAverage()));
220 }
catch (
final Exception e) {
221 metrics.put(
"error",
"Failed to collect system metrics: " + e.getMessage());
227 private static JettyHttpExchange asJetty(
final dev.rafex.ether.http.core.HttpExchange x) {
228 return (JettyHttpExchange) x;