feat: persistent container log collection, unified rotation, logs page redesign
- log_manager: add collect_container_logs (appends docker logs to container_<name>.log), get_container_log_lines, rotate_container_log, get_all_log_file_infos - app.py: new endpoints /api/logs/files (all log file sizes), /api/logs/containers/<name> (collect+return stored container logs); rotate endpoint now handles both service and container logs - Logs page: split into API Service Logs tab (python manager logs) and Container Logs tab (persistent docker stdout/stderr); Statistics tab shows both kinds with per-row rotate; each tab has a description explaining what it shows and where files live - wireguard_manager: test_connectivity peer_ip=None guard (already in previous commit, now rebuilt) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+30
-4
@@ -619,17 +619,43 @@ def get_log_statistics():
|
||||
|
||||
@app.route('/api/logs/rotate', methods=['POST'])
|
||||
def rotate_logs():
|
||||
"""Manually rotate logs."""
|
||||
"""Manually rotate logs (API service log or container log)."""
|
||||
try:
|
||||
data = request.get_json(silent=True) or {}
|
||||
service = data.get('service')
|
||||
|
||||
log_manager.rotate_logs(service)
|
||||
name = data.get('name') # e.g. 'wireguard' or 'container_cell-api'
|
||||
kind = data.get('kind', 'service') # 'service' or 'container'
|
||||
|
||||
if kind == 'container':
|
||||
container_name = name[len('container_'):] if name and name.startswith('container_') else name
|
||||
log_manager.rotate_container_log(container_name)
|
||||
else:
|
||||
log_manager.rotate_logs(name)
|
||||
return jsonify({"message": "Logs rotated successfully"})
|
||||
except Exception as e:
|
||||
logger.error(f"Error rotating logs: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/logs/files', methods=['GET'])
|
||||
def get_log_file_infos():
|
||||
"""List all stored log files (service + container) with sizes."""
|
||||
try:
|
||||
return jsonify(log_manager.get_all_log_file_infos())
|
||||
except Exception as e:
|
||||
logger.error(f"Error listing log files: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/logs/containers/<container_name>', methods=['GET'])
|
||||
def get_stored_container_logs(container_name):
|
||||
"""Collect latest docker logs into file and return last N lines."""
|
||||
try:
|
||||
tail = int(request.args.get('tail', 100))
|
||||
log_manager.collect_container_logs(container_name)
|
||||
lines = log_manager.get_container_log_lines(container_name, tail)
|
||||
return jsonify({'container': container_name, 'lines': lines})
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting stored container logs for {container_name}: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
# Network Services API
|
||||
@app.route('/api/dns/records', methods=['GET'])
|
||||
def get_dns_records():
|
||||
|
||||
Reference in New Issue
Block a user