mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-05-26 14:03:32 +00:00
fix(logs): include ledmatrix-web logs in viewer and log subprocess stderr on failure
Two bugs conspired to produce "check the logs" toasts with an empty log viewer: 1. The log viewer (both SSE stream and REST endpoint) only queried ledmatrix.service via journalctl. Web API errors are logged by the Flask process running as ledmatrix-web.service, so they never appeared in the viewer. Add -u ledmatrix-web.service to both calls; also add --output=short-iso so timestamps from the two services sort cleanly when interleaved. Use shutil.which-resolved absolute paths for sudo/journalctl (S607 compliance) in api_v3.py; fall back to known Pi paths if which returns None. 2. app.py: resolve journalctl and systemctl to absolute paths via shutil.which at module init (_JOURNALCTL, _SYSTEMCTL). Replace bare names in logs_generator() and the cached systemctl is-active check. Guard both sites: logs_generator yields a clear SSE error message and sleeps 60 s if journalctl is not found; the systemctl block is skipped entirely if systemctl is not found, leaving the cache at its last-known value. 3. When execute_system_action() ran a systemctl command that returned non-zero, only the return code was logged — result.stderr was silently discarded. Log it at ERROR level and include returncode and stderr in the JSON response so callers get actionable failure details. Same fix applied to the early-return start_display branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import os
|
||||
import re
|
||||
import stat
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
@@ -25,6 +26,9 @@ from src.web_interface.validators import (
|
||||
)
|
||||
from src.error_aggregator import get_error_aggregator
|
||||
|
||||
_SUDO = shutil.which('sudo')
|
||||
_JOURNALCTL = shutil.which('journalctl')
|
||||
|
||||
# Will be initialized when blueprint is registered
|
||||
config_manager = None
|
||||
plugin_manager = None
|
||||
@@ -1459,10 +1463,16 @@ def execute_system_action():
|
||||
result = subprocess.run(['sudo', 'systemctl', 'start', 'ledmatrix'],
|
||||
capture_output=True, text=True)
|
||||
logger.info("start_display (%s) returned code %d", mode, result.returncode)
|
||||
return jsonify({
|
||||
if result.returncode != 0 and result.stderr:
|
||||
logger.error("start_display (%s) stderr: %s", mode, result.stderr.strip())
|
||||
resp = {
|
||||
'status': 'success' if result.returncode == 0 else 'error',
|
||||
'message': 'Display started' if result.returncode == 0 else 'Failed to start display',
|
||||
})
|
||||
}
|
||||
if result.returncode != 0:
|
||||
resp['returncode'] = result.returncode
|
||||
resp['stderr'] = result.stderr.strip()
|
||||
return jsonify(resp)
|
||||
else:
|
||||
result = subprocess.run(['sudo', 'systemctl', 'start', 'ledmatrix'],
|
||||
capture_output=True, text=True)
|
||||
@@ -1564,10 +1574,16 @@ def execute_system_action():
|
||||
return jsonify({'status': 'error', 'message': 'Unknown action'}), 400
|
||||
|
||||
logger.info("system action '%s' returncode=%d", action, result.returncode)
|
||||
return jsonify({
|
||||
if result.returncode != 0 and result.stderr:
|
||||
logger.error("system action '%s' stderr: %s", action, result.stderr.strip())
|
||||
resp = {
|
||||
'status': 'success' if result.returncode == 0 else 'error',
|
||||
'message': 'Action completed' if result.returncode == 0 else 'Action failed; check logs for details',
|
||||
})
|
||||
}
|
||||
if result.returncode != 0:
|
||||
resp['returncode'] = result.returncode
|
||||
resp['stderr'] = result.stderr.strip()
|
||||
return jsonify(resp)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("execute_system_action failed: %s", e, exc_info=True)
|
||||
@@ -6425,9 +6441,12 @@ def list_plugin_assets():
|
||||
def get_logs():
|
||||
"""Get system logs from journalctl"""
|
||||
try:
|
||||
if not _SUDO or not _JOURNALCTL:
|
||||
return jsonify({'status': 'error', 'message': 'sudo or journalctl not found on this system'}), 503
|
||||
# Get recent logs from journalctl
|
||||
result = subprocess.run(
|
||||
['sudo', 'journalctl', '-u', 'ledmatrix.service', '-n', '100', '--no-pager'],
|
||||
[_SUDO, _JOURNALCTL, '-u', 'ledmatrix.service', '-u', 'ledmatrix-web.service',
|
||||
'-n', '100', '--no-pager', '--output=short-iso'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
@@ -6438,7 +6457,7 @@ def get_logs():
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'data': {
|
||||
'logs': logs_text if logs_text else 'No logs available from ledmatrix service'
|
||||
'logs': logs_text if logs_text else 'No logs available from ledmatrix or ledmatrix-web service'
|
||||
}
|
||||
})
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user