persist gateway metrics across restarts

This commit is contained in:
2026-06-29 16:54:29 +08:00
parent 283c14b6b4
commit 5f6fb3504d
5 changed files with 95 additions and 16 deletions
+61 -4
View File
@@ -360,6 +360,7 @@ function buildGatewayErrorBody(message) {
function createMonitor() {
return {
started_at: new Date().toISOString(),
persistent_since: null,
next_log_seq: 1,
next_request_seq: 1,
log_entries: [],
@@ -613,15 +614,70 @@ function buildRequestQueryFilters({ query, filter }) {
};
}
async function hydrateMonitorFromDisk(monitor, paths, requestHistoryLimit) {
const requestEntries = await readJsonlFile(paths.requestsPath);
async function hydrateMonitorFromDisk(monitor, paths, requestHistoryLimit, requestsDb = null) {
monitor.request_entries = [];
if (requestsDb) {
const totalsRow = requestsDb.prepare(`
SELECT
COUNT(*) AS total_proxy_request_count,
COALESCE(SUM(CASE WHEN inspected = 1 THEN 1 ELSE 0 END), 0) AS inspected_response_count,
COALESCE(SUM(CASE WHEN matched = 1 THEN 1 ELSE 0 END), 0) AS matched_response_count,
COALESCE(SUM(COALESCE(input_tokens, 0)), 0) AS input_tokens,
COALESCE(SUM(COALESCE(output_tokens, 0)), 0) AS output_tokens,
COALESCE(SUM(COALESCE(total_tokens, 0)), 0) AS total_tokens,
COALESCE(SUM(COALESCE(reasoning_tokens, 0)), 0) AS reasoning_tokens,
COALESCE(SUM(COALESCE(cached_tokens, 0)), 0) AS cached_tokens,
MIN(NULLIF(started_at, '')) AS persistent_since,
MAX(seq) AS max_seq
FROM requests
`).get();
monitor.total_proxy_request_count = totalsRow?.total_proxy_request_count || 0;
monitor.inspected_response_count = totalsRow?.inspected_response_count || 0;
monitor.matched_response_count = totalsRow?.matched_response_count || 0;
monitor.token_totals = {
input_tokens: totalsRow?.input_tokens || 0,
output_tokens: totalsRow?.output_tokens || 0,
total_tokens: totalsRow?.total_tokens || 0,
reasoning_tokens: totalsRow?.reasoning_tokens || 0,
cached_tokens: totalsRow?.cached_tokens || 0,
};
monitor.persistent_since = totalsRow?.persistent_since || null;
monitor.next_request_seq = Number.isInteger(totalsRow?.max_seq) ? totalsRow.max_seq + 1 : 1;
monitor.observed_reasoning_counts = {};
const reasoningRows = requestsDb.prepare(`
SELECT reasoning_tokens, COUNT(*) AS count
FROM requests
WHERE reasoning_tokens IS NOT NULL
GROUP BY reasoning_tokens
`).all();
for (const row of reasoningRows) {
if (!Number.isInteger(row?.reasoning_tokens)) {
continue;
}
monitor.observed_reasoning_counts[`${row.reasoning_tokens}`] = row.count || 0;
}
return;
}
const requestEntries = await readJsonlFile(paths.requestsPath);
monitor.next_request_seq = requestEntries.reduce((maxSeq, entry) => {
return Math.max(maxSeq, Number.isInteger(entry.seq) ? entry.seq + 1 : maxSeq);
}, 1);
for (const entry of requestEntries) {
monitor.total_proxy_request_count += 1;
if (entry.inspected) {
recordInspectedResponse(monitor, entry.reasoning_tokens ?? entry.usage?.reasoning_tokens ?? null, Boolean(entry.matched));
}
addTokenTotals(monitor, entry.usage);
}
const firstStartedAt = requestEntries
.map((entry) => `${entry?.started_at || ""}`.trim())
.filter(Boolean)
.sort()[0];
monitor.persistent_since = firstStartedAt || null;
}
function createMonitorRecorder(monitor) {
@@ -980,6 +1036,7 @@ function buildMetricsSnapshot(monitor) {
const inspectedResponseCount = monitor.inspected_response_count;
return {
started_at: monitor.started_at,
persistent_since: monitor.persistent_since,
total_proxy_request_count: monitor.total_proxy_request_count,
inspected_response_count: inspectedResponseCount,
matched_response_count: monitor.matched_response_count,
@@ -2684,9 +2741,9 @@ async function main() {
requestsDb,
server: null,
};
await hydrateMonitorFromDisk(monitor, runtime.paths, config.request_history_limit);
const importedCount = await importRequestsJsonlToDb(requestsDb, runtime.paths.requestsPath);
logger(`[start] hydrated token totals from jsonl path=${runtime.paths.requestsPath}`);
await hydrateMonitorFromDisk(monitor, runtime.paths, config.request_history_limit, requestsDb);
logger(`[start] hydrated persistent request metrics from db path=${runtime.paths.requestsDbPath}`);
logger(`[start] requests db ready path=${runtime.paths.requestsDbPath} imported_jsonl_rows=${importedCount}`);
const server = http.createServer(async (req, res) => {