feat(health): probe + classify engine on a 60s cron
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
41
lib/health/checker.js
Normal file
41
lib/health/checker.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import net from 'node:net';
|
||||
const SLOW_MS = 3000;
|
||||
|
||||
export function classify({ ok, reachable, latency, error }) {
|
||||
if (ok) return { status: latency > SLOW_MS ? 'warn' : 'ok', latency_ms: latency, detail: `${latency}ms` };
|
||||
if (reachable) return { status: 'warn', latency_ms: latency ?? null, detail: 'degraded' };
|
||||
return { status: 'down', latency_ms: null, detail: error || 'unreachable' };
|
||||
}
|
||||
|
||||
// Default probe: HTTP (status 2xx/3xx) or TCP connect. Only called with
|
||||
// operator-configured URLs from the registry — never user input.
|
||||
export async function probe(svc) {
|
||||
const started = Date.now();
|
||||
const type = svc.check?.type || 'http';
|
||||
try {
|
||||
if (type === 'tcp') {
|
||||
const u = new URL(svc.url);
|
||||
await new Promise((resolve, reject) => {
|
||||
const sock = net.connect({ host: u.hostname, port: Number(u.port) }, () => { sock.end(); resolve(); });
|
||||
sock.setTimeout(5000); sock.on('timeout', () => { sock.destroy(); reject(new Error('timeout')); });
|
||||
sock.on('error', reject);
|
||||
});
|
||||
return { ok: true, latency: Date.now() - started };
|
||||
}
|
||||
const base = svc.url.replace(/\/$/, '');
|
||||
const url = base + (svc.check?.path || '');
|
||||
const res = await fetch(url, { redirect: 'manual', signal: AbortSignal.timeout(6000) });
|
||||
const reachable = true;
|
||||
const ok = res.status >= 200 && res.status < 400;
|
||||
return { ok, reachable, latency: Date.now() - started };
|
||||
} catch (e) {
|
||||
return { ok: false, reachable: false, latency: Date.now() - started, error: e.code || e.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkAll(services, probeFn = probe) {
|
||||
return Promise.all(services.map(async svc => {
|
||||
const c = classify(await probeFn(svc));
|
||||
return { service_id: svc.id, ...c };
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user