feat: proper logging architecture — Docker rotation, persisted service logs, verbosity config

docker-compose.yml:
- Add json-file logging driver (max-size: 10m, max-file: 5) to all 13 containers
- Docker now owns container stdout/stderr rotation automatically
- Add ./data/logs:/app/api/data/logs volume to API — service logs now persist across restarts

log_manager.py:
- Remove container log collection hack (Docker handles it natively)
- Add set_service_level(service, level) — change log level at runtime without restart
- Add get_service_levels() — return current per-service levels
- Simplify get_all_log_file_infos to return only service log files

app.py:
- Add GET /api/logs/verbosity — return current per-service log levels
- Add PUT /api/logs/verbosity — update levels at runtime, persist to config/log_levels.json
- Load persisted log level overrides at startup from log_levels.json
- Simplify rotate endpoint (service logs only, container logs owned by Docker)

wireguard_manager.py:
- get_keys(): return empty strings if key files don't exist (prevents get_status crash
  when wg0.conf is missing at startup and falls through to generate_config)

Logs page (4 tabs):
- API Service Logs: structured JSON logs from Python managers, with search/filter/rotate panel
- Container Logs: live docker logs (read via existing /api/containers/<name>/logs endpoint)
- Verbosity Config: per-service level dropdowns, apply immediately + persist
- Health History: existing health poll table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 02:07:57 -04:00
parent 7b39331417
commit f848a1d056
6 changed files with 313 additions and 289 deletions
+3 -6
View File
@@ -191,13 +191,10 @@ export const logsAPI = {
getServiceLogs: (service, level = 'ALL', lines = 100) =>
api.get(`/api/logs/services/${service}`, { params: { level, lines } }),
searchLogs: (data) => api.post('/api/logs/search', data),
exportLogs: (data) => api.post('/api/logs/export', data),
getStatistics: (service) =>
api.get('/api/logs/statistics', service ? { params: { service } } : {}),
rotateLogs: (name, kind = 'service') => api.post('/api/logs/rotate', { name, kind }),
getLogFiles: () => api.get('/api/logs/files'),
getStoredContainerLogs: (containerName, tail = 100) =>
api.get(`/api/logs/containers/${containerName}`, { params: { tail } }),
rotateLogs: (service) => api.post('/api/logs/rotate', service ? { service } : {}),
getVerbosity: () => api.get('/api/logs/verbosity'),
setVerbosity: (levels) => api.put('/api/logs/verbosity', levels),
};
// Container Management API