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:
@@ -74,9 +74,13 @@ class WireGuardManager(BaseServiceManager):
|
||||
pass
|
||||
|
||||
def get_keys(self) -> Dict[str, str]:
|
||||
"""Return server public/private keys as base64 strings."""
|
||||
"""Return server public/private keys as base64 strings. Generates them if missing."""
|
||||
priv_file = os.path.join(self.keys_dir, 'private.key')
|
||||
pub_file = os.path.join(self.keys_dir, 'public.key')
|
||||
if not os.path.exists(priv_file):
|
||||
self._ensure_server_keys()
|
||||
if not os.path.exists(priv_file):
|
||||
return {'private_key': '', 'public_key': ''}
|
||||
with open(priv_file, 'rb') as f:
|
||||
priv = f.read()
|
||||
with open(pub_file, 'rb') as f:
|
||||
|
||||
Reference in New Issue
Block a user