Rendez votre service observable et arrêtable proprement : logs JSON, sondes liveness/readiness et gestion de SIGTERM.
Ouvrir cette leçon dans KodokonEn production, un log est destiné à une machine (Loki, Datadog, CloudWatch), pas à un humain : émettez du JSON par ligne (une ligne = un événement) avec un niveau, un horodatage ISO et des champs structurés - jamais d'interpolations libres impossibles à requêter. Détail méconnu : l'écriture sur stdout est synchrone vers un fichier ou un terminal, mais asynchrone vers un pipe. Un logger bavard peut donc bloquer l'event loop selon la destination - c'est la raison d'être de bibliothèques comme pino, qui sérialisent vite et peuvent déporter l'écriture.
function log(level, message, fields = {}) {
const entry = {
level,
message,
time: new Date().toISOString(),
pid: process.pid,
...fields,
};
process.stdout.write(JSON.stringify(entry) + "\n");
}
log("info", "server started", { port: 3000 });
log("error", "db unreachable", { retryInMs: 5000 });Distinguez deux sondes. La liveness répond à « le processus doit-il être redémarré ? » : elle doit rester triviale, sans dépendance externe. La readiness répond à « puis-je recevoir du trafic ? » : elle peut vérifier la base de données, et surtout passer à 503 pendant l'arrêt, pour que le load balancer retire l'instance avant la coupure. Confondre les deux fait redémarrer des processus sains dès qu'une dépendance tousse.
import { createServer } from "node:http";
let ready = true;
const server = createServer((req, res) => {
if (req.url === "/healthz") {
res.writeHead(ready ? 200 : 503);
res.end(ready ? "ok" : "draining");
return;
}
res.end("hello");
});
server.listen(3000);
function shutdown() {
ready = false;
server.closeIdleConnections();
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 10_000).unref();
}
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);Le déroulé d'un arrêt propre : l'orchestrateur envoie SIGTERM ; vous basculez la readiness à 503 ; server.close() refuse les nouvelles connexions et attend la fin des requêtes en cours ; closeIdleConnections() ferme les connexions keep-alive inactives qui, sinon, retiendraient close() indéfiniment. Le timer marqué unref() sert de filet de sécurité : il tue le processus après 10 secondes, sans empêcher à lui seul le processus de se terminer plus tôt.
process.on("uncaughtException", (err) => {
const entry = {
level: "fatal",
message: err.message,
stack: err.stack,
time: new Date().toISOString(),
};
process.stderr.write(JSON.stringify(entry) + "\n");
process.exit(1);
});
process.on("unhandledRejection", (reason) => {
throw reason;
});