mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-10-19 14:17:20 +02:00
fix: contain upstream failures and back off repeated retries
This commit is contained in:
1 parent
74fe405e1b
commit
37d10eed20
21 files changed
+511
-108
No files matched your search
@@ -0,0 +1,63 @@
|
||||
const { spawn, execFileSync } = require("node:child_process");
|
||||
const { createInterface } = require("node:readline");
|
||||
|
||||
function logSummary() {
|
||||
return { completed: 0, interrupted: 0, completed5xx: 0, fatalErrors: 0, errors: 0,
|
||||
warnings: 0, tokenRefreshFailures: 0, missingFiles: 0, rateLimited: 0, connectionResets: 0 };
|
||||
}
|
||||
function observeLine(summary, line) {
|
||||
// Older releases rely on Node's stderr. New releases also emit a redacted
|
||||
// process_fatal_error event before Node terminates.
|
||||
const match = /^\S+ (INFO|WARN|ERROR) \[([^\]]+)\] .*? (\{.*\})$/.exec(line);
|
||||
if (!match) {
|
||||
if (/Unhandled ['"]error['"] event|unhandled(?:promise)?rejection|FATAL ERROR|heap out of memory/i.test(line)) summary.fatalErrors++;
|
||||
return;
|
||||
}
|
||||
let data;
|
||||
try { data = JSON.parse(match[3]); } catch { return; }
|
||||
const [, level, scope] = match;
|
||||
if (level === "ERROR") summary.errors++;
|
||||
if (level === "WARN") summary.warnings++;
|
||||
if (data.code === "process_fatal_error" || /startup failed/i.test(line)) summary.fatalErrors++;
|
||||
if (data.code === "token_refresh_failed") summary.tokenRefreshFailures++;
|
||||
if (scope === "anonymized-file" && data.code === "file_not_found") summary.missingFiles++;
|
||||
if (scope === "requests" && typeof data.ms === "number") {
|
||||
if (data.outcome === "completed") { summary.completed++; if (data.status >= 500) summary.completed5xx++; }
|
||||
else summary.interrupted++;
|
||||
if (data.status === 429) summary.rateLimited++;
|
||||
}
|
||||
if (/ECONNRESET|socket hang up/i.test(line)) summary.connectionResets++;
|
||||
}
|
||||
async function readLogs(name, since) {
|
||||
const summary = logSummary();
|
||||
const child = spawn("docker", ["logs", "--since", since, name], { stdio: ["ignore", "pipe", "pipe"] });
|
||||
const completion = new Promise((resolve, reject) => {
|
||||
child.once("error", reject);
|
||||
child.once("close", code => code === 0 ? resolve() : reject(new Error("docker logs failed")));
|
||||
});
|
||||
for (const output of [child.stdout, child.stderr]) {
|
||||
createInterface({ input: output }).on("line", line => observeLine(summary, line));
|
||||
}
|
||||
await completion;
|
||||
return summary;
|
||||
}
|
||||
function containerSummary(container) {
|
||||
return { name: container.Name.replace(/^\//, ""), healthy: container.State.Health?.Status === "healthy",
|
||||
running: container.State.Running, restartCount: container.RestartCount,
|
||||
oomKilled: container.State.OOMKilled, startedAt: container.State.StartedAt };
|
||||
}
|
||||
async function main(minutes) {
|
||||
if (!Number.isInteger(minutes) || minutes < 1 || minutes > 1440) throw new Error("Minutes must be between 1 and 1440");
|
||||
const since = new Date(Date.now() - minutes * 60000).toISOString();
|
||||
const names = execFileSync("docker", ["ps", "-a", "--filter", "label=com.docker.compose.project=anonymous_github", "--format", "{{.Names}}"], { encoding: "utf8" })
|
||||
.trim().split("\n").filter(name => /^anonymous_github-(anonymous_github|streamer)-\d+$/.test(name));
|
||||
if (!names.length) throw new Error("No application containers found");
|
||||
const containers = JSON.parse(execFileSync("docker", ["inspect", ...names], { encoding: "utf8" }));
|
||||
const services = [];
|
||||
for (const container of containers) services.push({ ...containerSummary(container), logs: await readLogs(container.Name, since) });
|
||||
console.log(JSON.stringify({ checkedAt: new Date().toISOString(), minutes, services }, null, 2));
|
||||
}
|
||||
module.exports = { logSummary, observeLine, containerSummary };
|
||||
if (require.main === module) main(Number(process.argv[2] || 15)).catch(() => {
|
||||
console.error("Production log check failed; check Docker access and the requested window."); process.exitCode = 1;
|
||||
});
|
||||
Reference in new issue
Block a user