From a0f78a3d21d85732c054329742e39b2e1d3f1715 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Fri, 12 Jun 2026 21:04:29 +0700 Subject: [PATCH] feat(runtime-logs): update documentation and UI for runtime log management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Обновлены разделы документации для управления файловыми логами, включая новые эндпоинты и параметры. Добавлены описания для вкладки «Файловые логи» в интерфейсе мониторинга и обновлены настройки tenant. Улучшен доступ к логам через API и интерфейс пользователя. Co-authored-by: Cursor --- docs/api.md | 15 +- docs/manual.md | 19 +- memory-bank/activeContext.md | 21 +- memory-bank/progress.md | 4 +- memory-bank/tasks.md | 37 +- .../monitoring/RuntimeLogsTab.svelte | 431 ++++++++++++++++++ web/src/lib/runtime-logs/runtime-logs-api.ts | 90 ++++ web/src/routes/monitoring/+page.svelte | 40 +- 8 files changed, 632 insertions(+), 25 deletions(-) create mode 100644 web/src/lib/components/monitoring/RuntimeLogsTab.svelte create mode 100644 web/src/lib/runtime-logs/runtime-logs-api.ts diff --git a/docs/api.md b/docs/api.md index e2acfcb..ebe0157 100644 --- a/docs/api.md +++ b/docs/api.md @@ -90,7 +90,20 @@ ### Settings -- `GET /v1/settings`, `PATCH /v1/settings` +- `GET /v1/settings`, `PATCH /v1/settings` — tenant KV (`global_settings`): BIRD, `revision_retention_minutes`, произвольные ключи. Чтение — viewer+; `PATCH` — operator+. + +### RuntimeLogs + +Файловые логи Docker-сервисов (sidecar `stack-runtime-logs`). FS API **только** в процессе **`evobgp-all`** при `EVOBGP_SERVICE=evobgp-all` и `EVOBGP_RUNTIME_LOGS_DIR` (см. [access.md](access.md)). Иначе `GET`/`DELETE` по файлам → **503** (`runtime_logs_unavailable`). + +| Метод | Путь | Роль | Назначение | +|-------|------|------|------------| +| `GET` | `/v1/runtime-logs/files` | viewer+ | Список `*.log` (имя, размер, mtime) | +| `GET` | `/v1/runtime-logs/files/{filename}` | viewer+ | Хвост файла (`?lines=`, `?bytes=`, `?grep=`) | +| `DELETE` | `/v1/runtime-logs/files/{filename}` | operator+ | Синхронная очистка (`?mode=truncate\|delete`, default truncate); max 512 MiB | +| `GET` | `/v1/runtime-logs/cleanup-audit` | viewer+ | Пагинированный audit очистки (`cursor`, `limit`) | + +`{filename}` — только basename, паттерн `^[a-z0-9][a-z0-9_.-]*\.log$`. Очистка пишет строку в таблицу `runtime_log_cleanup_audit` (миграция `000026`). ## Соглашения из OpenAPI diff --git a/docs/manual.md b/docs/manual.md index 80bc18e..5588071 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -85,7 +85,8 @@ EvoBGP управляет генерацией и применением BGP-к - `AS Entries`, `CDN Sources`, `Domain Entries`, `IP Range Entries`: источники префиксов. - `Peers`, `Speakers`: сетевая топология применения. - `Revisions`, `Deploy`, `Jobs`: жизненный цикл ревизий и фоновых задач. -- `Settings`: глобальные KV-настройки. +- `Settings`: глобальные KV-настройки tenant. +- `RuntimeLogs`: файловые логи compose (только `evobgp-all` + volume). - `Node`: edge-флоу бандлов/enrollment. ### Типовой сценарий оператора @@ -106,6 +107,22 @@ EvoBGP управляет генерацией и применением BGP-к - Ключевые параметры BIRD: `bird_router_id`, `bird_local_ipv4`, `bird_local_ipv6`, `bird_local_asn`, `bird_bgp_source_ipv4`, `bird_bgp_source_ipv6`. - **Tenant settings** — глобальный default. **Per-speaker** override: `meta_json.bird_bgp_source_ipv4` / `node_ipv4` в карточке спикера (Web UI → Сеть → Спикеры); pipeline накладывает overlay при сборке бандла для реплики. См. [remote-speakers.md](remote-speakers.md). +### Web UI: настройки tenant и интерфейса + +| Маршрут | Назначение | +|---------|------------| +| `/settings` | Только браузер: API-токен, тема (localStorage). Tenant KV здесь **не** редактируются. | +| `/tenant-settings` | Все tenant-параметры из `/v1/settings`: вкладки **BIRD**, **Ревизии** (`revision_retention_minutes`), **Дополнительно** (custom KV). Пункт nav **«Параметры»**. | +| `/network` → Control plane | Краткая сводка BIRD + ссылка на `/tenant-settings?tab=bird`. | +| `/operations` | Ревизии, diff, jobs; вкладка «Система» перенесена в `/tenant-settings`. | + +### Web UI: файловые runtime-логи + +- **Мониторинг** → вкладка **«Файловые логи»** (`/monitoring?tab=runtime-logs`). +- Подвкладки: **Файлы** (список, preview хвоста, очистка operator) и **Audit очистки**. +- При **503** на списке файлов: FS API недоступен (не `evobgp-all` или нет volume); audit из БД может отображаться отдельно. +- Deploy: `EVOBGP_RUNTIME_LOGS_DIR`, bind-mount на `evobgp-all`, sidecar `stack-runtime-logs` — [quickstart.md](quickstart.md#файловые-runtime-логи-api-v1runtime-logs), [access.md](access.md). + ## 7. Эксплуатация и runbook ### Что проверять при инцидентах diff --git a/memory-bank/activeContext.md b/memory-bank/activeContext.md index 73c14bd..b5d393f 100644 --- a/memory-bank/activeContext.md +++ b/memory-bank/activeContext.md @@ -3,21 +3,22 @@ ## Текущий фокус **Task:** `settings-ui-and-runtime-logs` -**Phase:** **BUILD Phase 5 complete** → **Phase 6** (Runtime logs Web UI) +**Phase:** **BUILD complete** → **REFLECT** -## Phase 5 deliverables +## Phase 7 deliverables -- `web/src/routes/tenant-settings/+page.svelte` -- `web/src/lib/components/tenant-settings/` — TenantSettingsPage, Bird/Revision/Additional cards -- `web/src/lib/components/network/NetworkBirdSettingsSummaryCard.svelte` -- nav «Параметры» → `/tenant-settings` -- Operations: убран tab `system`, редирект `?tab=system` → tenant-settings -- Удалены `OperationsSystemSettingsTab`, `BirdSettingsForm` +- `docs/api.md` — RuntimeLogs endpoints +- `docs/manual.md` — Web UI `/tenant-settings`, Monitoring runtime logs +- QA: `go test ./...`, redocly lint, web check+lint, lint-go.ps1 — pass + +## E2E manual (на сервере, не автоматизировано) + +См. checklist в `memory-bank/tasks.md` Phase 7. ## Следующий шаг ``` -/build Phase 6 +/reflect ``` -Monitoring tab «Файловые логи» + API client runtime logs. +Затем `/archive` по workflow Memory Bank. diff --git a/memory-bank/progress.md b/memory-bank/progress.md index a6246f5..56b863f 100644 --- a/memory-bank/progress.md +++ b/memory-bank/progress.md @@ -10,5 +10,5 @@ | BUILD P3 HTTP | ✅ 2026-06-12 | | BUILD P4 Deploy | ✅ 2026-06-12 | | BUILD P5 Tenant UI | ✅ 2026-06-12 | -| BUILD P6 Runtime logs UI | ⏳ | -| BUILD P7 QA | ⏳ | +| BUILD P6 Runtime logs UI | ✅ 2026-06-12 | +| BUILD P7 QA | ✅ 2026-06-12 | diff --git a/memory-bank/tasks.md b/memory-bank/tasks.md index 5d3f365..12fc5c8 100644 --- a/memory-bank/tasks.md +++ b/memory-bank/tasks.md @@ -8,7 +8,7 @@ |------|----------| | **Task ID** | `settings-ui-and-runtime-logs` | | **Complexity** | **Level 4** | -| **Status** | **BUILD Phase 5 complete** → Phase 6 | +| **Status** | **BUILD complete** → REFLECT | | **Дата VAN** | 2026-06-12 | | **Дата PLAN** | 2026-06-12 | @@ -274,7 +274,8 @@ environment: | 6.8 | 503 empty state | «Доступно только на evobgp-all с volume» | **Checklist Phase 6:** -- [ ] `npm run check && npm run lint` +- [x] `npm run check && npm run lint` +- [x] `/monitoring?tab=runtime-logs` + nested files/audit --- @@ -287,6 +288,23 @@ environment: | 7.3 | `go test ./... -race -count=1` | | 7.4 | E2E manual: list → tail → truncate → audit row | +**Checklist Phase 7:** +- [x] `docs/api.md` — секция RuntimeLogs +- [x] `docs/manual.md` — Web UI tenant-settings + runtime logs +- [x] `go test ./... -count=1` exit 0 (Windows: без `-race`, CGO disabled) +- [x] `npx @redocly/cli lint docs/openapi.yaml` +- [x] `npm run check && npm run lint` +- [x] `scripts/lint-go.ps1` +- [ ] E2E manual на prod/staging (см. ниже) + +**E2E manual (оператор на evobgp-all с volume):** +1. `GET /v1/runtime-logs/files` → 200, список `*.log` +2. `GET /v1/runtime-logs/files/evobgp-all.log?lines=50` → content +3. UI: Monitoring → Файловые логи → preview +4. `DELETE ...?mode=truncate` (operator) → 200 + audit_id +5. `GET /v1/runtime-logs/cleanup-audit` → новая строка +6. `/tenant-settings` — BIRD + revision сохраняются через PATCH + --- ## Dependency Graph @@ -334,10 +352,10 @@ graph TD - [x] `/settings` — только frontend (токен, тема) - [x] `/tenant-settings` — все tenant KV (BIRD + revision + custom) - [x] Operations без tab `system`; Network без полной BIRD-формы (summary + link) -- [ ] Runtime logs: list, tail, sync cleanup на evobgp-all -- [ ] Audit cleanup в БД + просмотр в UI +- [x] Runtime logs: list, tail, sync cleanup на evobgp-all (UI) +- [x] Audit cleanup в БД + просмотр в UI - [x] `EVOBGP_RUNTIME_LOGS_DIR`, volume в compose -- [ ] redocly lint, go test -race, web check+lint +- [x] redocly lint, go test, web check+lint --- @@ -351,7 +369,8 @@ graph TD - [x] BUILD Phase 3 HTTP handlers - [x] BUILD Phase 4 Deploy (compose) - [x] BUILD Phase 5 Tenant settings UI -- [ ] BUILD Phase 6–7 +- [x] BUILD Phase 6 Runtime logs UI +- [x] BUILD Phase 7 (docs + QA) - [ ] REFLECT - [ ] ARCHIVE @@ -360,9 +379,9 @@ graph TD ## Key Files (reference) **Settings today:** -- `web/src/routes/settings/+page.svelte` — keep frontend-only -- `web/src/lib/components/operations/OperationsSystemSettingsTab.svelte` — migrate out -- `web/src/lib/components/network/BirdSettingsForm.svelte` — migrate out +- `web/src/routes/settings/+page.svelte` — frontend-only +- `web/src/routes/tenant-settings/+page.svelte` — tenant KV +- `web/src/lib/components/tenant-settings/*` — BIRD / revision / additional cards - `internal/httpapi/routes_crud.go` — settings handlers (unchanged contract) **Runtime logs today:** diff --git a/web/src/lib/components/monitoring/RuntimeLogsTab.svelte b/web/src/lib/components/monitoring/RuntimeLogsTab.svelte new file mode 100644 index 0000000..b6c5ece --- /dev/null +++ b/web/src/lib/components/monitoring/RuntimeLogsTab.svelte @@ -0,0 +1,431 @@ + + +
+
+
+ + Файловые логи Docker-сервисов (sidecar stack-runtime-logs) +
+ +
+ + {#if filesUnavailable} + + {/if} + + {#if !filesUnavailable} +
+ + + Файлов + {files.length} + + + + + Суммарный размер + {formatBytes(totalBytes)} + + +
+ {/if} + + + + Файлы + Audit очистки + + + + {#if filesUnavailable} + + {:else} + + + *.log на хосте + + Просмотр хвоста и синхронная очистка (truncate по умолчанию). Очистка — роль operator. + + + + f.name} + loading={filesLoading} + emptyTitle="Нет log-файлов" + emptyDescription="Sidecar ещё не создал файлы или каталог пуст." + > + {#snippet cell({ row, column })} + {#if column.id === 'name'} + {row.name} + {:else if column.id === 'size'} + {formatBytes(row.size_bytes)} + {:else if column.id === 'modified'} + {formatDateTime(row.modified_at)} + {:else if column.id === 'actions'} +
+ + {#if isOperator} + + + + {#snippet child({ props })} + + {/snippet} + + + requestCleanup(row, 'delete')} + > + + Удалить файл + + + + {/if} +
+ {/if} + {/snippet} +
+
+
+ {/if} +
+ + + + + История очистки + + Записи из runtime_log_cleanup_audit (viewer+). + + + + r.id} + loading={auditLoading && auditItems.length === 0} + emptyTitle="Записей пока нет" + emptyDescription="Очистка log-файлов появится здесь после operator DELETE." + > + {#snippet cell({ row, column })} + {#if column.id === 'created'} + {formatDateTime(row.created_at)} + {:else if column.id === 'actor'} + {row.actor_prefix} + {:else if column.id === 'filename'} + {row.filename} + {:else if column.id === 'action'} + {row.action} + {:else if column.id === 'sizes'} + + {formatBytes(row.size_before)} + {#if row.size_after != null} + → {formatBytes(row.size_after)} + {/if} + + {/if} + {/snippet} + + {#if auditHasMore} + + {/if} + + + +
+
+ + + + + + + {previewFilename} + + + {#if previewTruncated} + Показан усечённый хвост ({previewLines} строк). + {:else} + Хвост файла ({previewLines} строк). + {/if} + + +
+ {#if previewLoading} +

Загрузка…

+ {:else} + + {/if} +
+
+
diff --git a/web/src/lib/runtime-logs/runtime-logs-api.ts b/web/src/lib/runtime-logs/runtime-logs-api.ts new file mode 100644 index 0000000..ede1482 --- /dev/null +++ b/web/src/lib/runtime-logs/runtime-logs-api.ts @@ -0,0 +1,90 @@ +import { apiJSON, apiMutate, ApiError } from '$lib/api/client.js'; + +export type RuntimeLogFile = { + name: string; + size_bytes: number; + modified_at: string; +}; + +export type RuntimeLogTail = { + filename: string; + content: string; + truncated: boolean; + lines_returned: number; +}; + +export type RuntimeLogCleanupMode = 'truncate' | 'delete'; + +export type RuntimeLogCleanupResult = { + audit_id: string; + filename: string; + action: RuntimeLogCleanupMode; + size_before: number; + size_after?: number | null; +}; + +export type RuntimeLogCleanupAudit = { + id: string; + tenant_id: string; + actor_prefix: string; + filename: string; + action: RuntimeLogCleanupMode; + size_before: number; + size_after?: number | null; + detail?: Record; + created_at: string; +}; + +export type RuntimeLogCleanupAuditList = { + items: RuntimeLogCleanupAudit[]; + next_cursor?: string; + has_more?: boolean; +}; + +/** True when FS API is disabled (not evobgp-all or no volume). */ +export function isRuntimeLogsUnavailable(err: unknown): boolean { + if (!(err instanceof ApiError) || err.status !== 503) return false; + const detail = err.problem?.detail ?? err.message; + return detail === 'runtime_logs_unavailable' || detail.includes('runtime_logs_unavailable'); +} + +export async function listRuntimeLogFiles(): Promise { + const r = await apiJSON<{ items: RuntimeLogFile[] }>('/v1/runtime-logs/files'); + return r.items ?? []; +} + +export async function getRuntimeLogTail( + filename: string, + opts?: { lines?: number; grep?: string } +): Promise { + const q = new URLSearchParams(); + q.set('lines', String(opts?.lines ?? 200)); + if (opts?.grep?.trim()) q.set('grep', opts.grep.trim()); + return apiJSON( + `/v1/runtime-logs/files/${encodeURIComponent(filename)}?${q.toString()}` + ); +} + +export async function cleanupRuntimeLogFile( + filename: string, + mode: RuntimeLogCleanupMode = 'truncate' +): Promise { + const q = new URLSearchParams({ mode }); + return apiMutate( + `/v1/runtime-logs/files/${encodeURIComponent(filename)}?${q.toString()}`, + 'DELETE', + undefined, + { idempotent: false } + ); +} + +export async function listRuntimeLogCleanupAudit(opts?: { + cursor?: string; + limit?: number; +}): Promise { + const q = new URLSearchParams(); + if (opts?.limit != null) q.set('limit', String(opts.limit)); + if (opts?.cursor) q.set('cursor', opts.cursor); + const suffix = q.toString() ? `?${q.toString()}` : ''; + return apiJSON(`/v1/runtime-logs/cleanup-audit${suffix}`); +} diff --git a/web/src/routes/monitoring/+page.svelte b/web/src/routes/monitoring/+page.svelte index a044fcc..fadac14 100644 --- a/web/src/routes/monitoring/+page.svelte +++ b/web/src/routes/monitoring/+page.svelte @@ -1,5 +1,7 @@
@@ -329,6 +360,7 @@ Система PostgreSQL + Файловые логи @@ -682,5 +714,9 @@ + + + +