Repository navigation
feat(console): filter usage and logs by API key - #434
Conversation
request_logs now records which named console API key issued each request (migration 025 adds api_key_id). Console-key traffic stays unattributed, so the key filter still sees it as one bucket. The four aggregates behind the two pages take an api_key filter: GET /api/logs/requests, /api/logs/stats, /api/logs/usage and /api/usage/cost. Stats and usage cache keys include it so a per-key window never serves another key's numbers. Logs gets a key filter, a 密钥 column and the key in the request detail; Usage gets one key picker above both tabs, so 统计 and 额度 stay in sync. Agent: hermes Verify: go test ./... (golang:1.25 container) + go vet + npm run lint + tsc -b + browser check of /logs and /usage
internal/webui/static was built against npm-installed semver-maximum packages (vite 8.3.5 / rolldown 1.2.15 / @heroui 3.2.7), not the versions pinned in frontend/package-lock.json (8.2.2 / 1.2.5 / 3.2.4). Vite hashes asset filenames by content, so CI's npm-ci build produced different names and the static-is-up-to-date check failed. Rebuilt in node:22 with npm ci + npm run sync so the committed assets match what CI regenerates. Agent: hermes Verify: npm ci + npm run sync in node:22 container
|
静态资源这轮对了, 还有两处筛选对不齐,合之前需要补上:
另外两个小的,不挡合并:用量页的密钥选择没有进 URL,刷新会丢; |
Review follow-up. Usage -> Logs drill-down dropped the active key filter, so a row that showed one key's numbers opened a list of every key's requests. All three call sites now go through openLogsFor, which merges api_key and row.filter. The Usage key picker also lived only in component state, so a reload lost it. It now mirrors ?api_key like the Logs page does. GET /api/logs/requests trimmed its api_key while stats and usage did not; aligned it with strings.TrimSpace. Agent: hermes Verify: go build + go test internal/console,internal/store + tsc -b + npm lint + browser check of drill-down and reload
|
@caigee-cmd 谢谢 review,前三条已修( 1. 用量页 → 日志的 日志页的密钥选择器回显 2. 用量页密钥选择没进 URL 已修。只镜像 3. 关于第 2 条(日志页汇总),我这边对不上,想跟你确认位置。 当前分支和 所以"选中一把密钥后上面的汇总仍是全部流量"这个现象我在当前版本复现不到。有两种可能:
如果确实是日志页该有一块汇总而我漏了,麻烦指出是哪个元素或给我个截图,我补上。我不想凭猜去新加一块 UI。 本 PR 由 AI 助手(Hermes)代发,内容已由本人在本地跑通并 review。 |
|
对,第 2 条是我看错了页面,收回。 日志页和 另外三处我按
还剩一个同类的小问题,不挡合并: CI 这轮还在跑,过了就可以合。 |
Summary
Request logs now record which named console API key issued each request, and the four aggregates behind the Logs and Usage pages accept an
api_keyfilter.Because every aggregate funnels through
buildRequestLogWhere, a key filter and an unfiltered window stay consistent with each other and with the logs drill-down.internal/ ├── store/ │ ├── migrations.go # 025 adds api_key_id TEXT NOT NULL DEFAULT '' + index │ └── request_logs.go # column in insert/update/select, filter clause, cost aggregate ├── executor/prepare.go # IdentityAPIKeyID(identity) written at request start ├── logs/recorder.go # APIKeyID threaded through Stats/Usage/CostBreakdown + cache keys └── console/ ├── logs.go # ?api_key on requests / stats / usage └── usage.go # ?api_key on cost frontend/src/ ├── hooks/useApiKeyOptions.ts # key list → filter options (new) ├── pages/LogsPage.tsx # key filter, 密钥 column, detail field └── pages/UsagePage.tsx # one key picker above both tabsTwo decisions worth calling out:
api_key_idempty instead of inventing a sentinel. A filter is always a named key, so "all keys" is the only view that includes console traffic.UPDATE request_logsusesapi_key_id = COALESCE(NULLIF(?, ''), api_key_id)so a finish-write that omits the key cannot erase what the start-write recorded.Evidence
Before: request rows carried no key attribution, and the only key-scoped view was the whole console.
After: end-to-end test in
internal/api/logs_api_key_test.gocreates a key, sends one keyed and one console-key request through the real handler, then asserts all five read paths:Live check on a throwaway instance (2 keys, 4 seeded rows) — Logs page goes 1–4 / 4 → 1–1 / 1 when
client-ais selected, URL carriesapi_key=key_0fad…; Usage 统计 goes 4 requests / 298k tokens → 1 / 62k, and the 额度 tab shows 0.40 credits for the same key.Full suite (
golang:1.25):On a Windows host a handful of
internal/apiandinternal/storetests fail onTempDircleanup (SQLite file still open) and on a0600file-mode assertion; both reproduce on unmodifiedmainand pass under Linux.Merge Danger
Door: two-way — reverting drops the column from console reads; the migration row stays applied but is inert.
Blast Radius: request-log schema
Migration 025 only adds a defaulted column plus an index, so existing databases migrate in place. Rows written before this PR report an empty
api_key_idand are only visible in the unfiltered view — they are not backfillable, because the key was never recorded. Old Stats/Usage cache entries are unaffected sinceapi_key_idis now part of the cache key.本 PR 由 AI 助手(Hermes)代发,内容已由本人在本地跑通并 review。