feat: write runtime slo daily reports
This commit is contained in:
@@ -87,6 +87,30 @@ function uniquePaths(paths) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {
|
||||
writeReport: false,
|
||||
reportDir: process.env.MEMIND_RUNTIME_REPORT_DIR || '',
|
||||
};
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const item = argv[i];
|
||||
if (item === '--write-report') args.writeReport = true;
|
||||
else if (item === '--report-dir') args.reportDir = String(argv[++i] ?? args.reportDir);
|
||||
else if (item === '--help' || item === '-h') args.help = true;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
console.log([
|
||||
'Usage:',
|
||||
' node scripts/runtime-slo-report.mjs [--write-report] [--report-dir <dir>]',
|
||||
'',
|
||||
'Default behavior is read-only and writes nothing.',
|
||||
'--write-report writes JSON and Markdown snapshots to an operations report directory.',
|
||||
].join('\n'));
|
||||
}
|
||||
|
||||
function tableCountQueries() {
|
||||
return [
|
||||
['h5_users', 'users'],
|
||||
@@ -178,6 +202,11 @@ function summarizeRuntime(runtimeJson) {
|
||||
const envFile = process.env.MEMIND_ENV_FILE || path.join(process.cwd(), '.env');
|
||||
const appRoot = process.env.MEMIND_APP_ROOT || path.dirname(path.resolve(envFile));
|
||||
loadEnvFile(envFile);
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
if (args.help) {
|
||||
printHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const publicBase = (process.env.H5_PUBLIC_BASE_URL || 'https://mm.tkmind.cn').replace(/\/$/, '');
|
||||
const redisUrl = process.env.MEMIND_RUNTIME_REDIS_URL || 'redis://127.0.0.1:6379/0';
|
||||
@@ -244,8 +273,67 @@ const report = {
|
||||
database: false,
|
||||
mindSpace: false,
|
||||
redis: false,
|
||||
report: Boolean(args.writeReport),
|
||||
},
|
||||
};
|
||||
|
||||
function markdownReport(payload) {
|
||||
const workers = payload.runtime?.workers ?? [];
|
||||
const lines = [
|
||||
`# Memind Runtime SLO Report`,
|
||||
'',
|
||||
`- checkedAt: ${payload.checkedAt}`,
|
||||
`- ok: ${payload.ok}`,
|
||||
`- failures: ${payload.failures.length ? payload.failures.join(', ') : 'none'}`,
|
||||
`- publicBase: ${payload.publicBase}`,
|
||||
`- appRoot: ${payload.appRoot}`,
|
||||
'',
|
||||
'## Workers',
|
||||
'',
|
||||
'| worker | healthy | active | errors | first-token 5m p50/p95 | first-token 1h p50/p95 | metricsFresh | score |',
|
||||
'| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |',
|
||||
];
|
||||
for (const worker of workers) {
|
||||
lines.push([
|
||||
worker.id,
|
||||
worker.healthy,
|
||||
worker.activeStreams,
|
||||
worker.streamErrorCount,
|
||||
`${worker.firstToken5m?.p50Ms ?? 0}/${worker.firstToken5m?.p95Ms ?? 0}`,
|
||||
`${worker.firstToken1h?.p50Ms ?? 0}/${worker.firstToken1h?.p95Ms ?? 0}`,
|
||||
worker.metricsFresh,
|
||||
worker.score,
|
||||
].join(' | ').replace(/^/, '| ').replace(/$/, ' |'));
|
||||
}
|
||||
lines.push(
|
||||
'',
|
||||
'## Tool Queue',
|
||||
'',
|
||||
'```json',
|
||||
JSON.stringify(payload.runtime?.toolQueue ?? null, null, 2),
|
||||
'```',
|
||||
'',
|
||||
'## Writes',
|
||||
'',
|
||||
'```json',
|
||||
JSON.stringify(payload.writes, null, 2),
|
||||
'```',
|
||||
'',
|
||||
);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
if (args.writeReport) {
|
||||
const reportDir = path.resolve(args.reportDir || path.join(appRoot, 'reports', 'runtime-slo'));
|
||||
fs.mkdirSync(reportDir, { recursive: true });
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
report.reportFiles = {
|
||||
json: path.join(reportDir, `${stamp}.json`),
|
||||
markdown: path.join(reportDir, `${stamp}.md`),
|
||||
};
|
||||
fs.writeFileSync(report.reportFiles.json, `${JSON.stringify(report, null, 2)}\n`);
|
||||
fs.writeFileSync(report.reportFiles.markdown, markdownReport(report));
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
process.exit(report.ok ? 0 : 1);
|
||||
|
||||
Reference in New Issue
Block a user