From e90cf85f696fb9998117df9fc873cf97a917d615 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Fri, 26 Dec 2025 21:07:16 +0100 Subject: [PATCH] feat: implement working system logs viewer in system-hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed and enhanced the system logs functionality with real-time log viewing and color-coded log levels. 🔧 Backend Fix (RPCD) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Problem: - get_logs() function returned empty array - Logs were piped through while loop creating a subshell - json_add_string in subshell didn't affect main JSON output Solution: - Use temporary file to collect logs - Read from file outside of subshell - Properly build JSON array with all log lines Implementation: ```sh # Store logs in temporary file logread | tail -n "$lines" > "$tmpfile" # Read from file (no subshell issue) while IFS= read -r line; do json_add_string "" "$line" done < "$tmpfile" ``` Testing: ✅ ubus call luci.system-hub get_logs '{"lines": 100}' → Returns real logs ✅ Filter parameter works: get_logs '{"filter": "error"}' ✅ Temporary files cleaned up after use 🎨 Frontend Enhancement (logs.js) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Added color-coded log level display: Features: 1. **Color-coded by severity:** - 🔴 Errors (red): error, err, fatal, crit - 🟡 Warnings (orange): warn, warning - 🔵 Info (blue): info, notice - 🟣 Debug (purple): debug - 🟢 Success (green): success, ok 2. **Visual enhancements:** - Colored left border for each log line - Semi-transparent background matching log level - JetBrains Mono font for better readability - Proper line spacing and padding 3. **Empty state:** - Friendly message when no logs available - Different message for empty search results - Large icon for better UX Implementation: ```javascript renderLogLine: function(line) { // Detect log level from keywords if (line.includes('error')) { color = '#ef4444'; // Red bgColor = 'rgba(239, 68, 68, 0.1)'; } // ... other levels return E('div', { 'style': 'border-left: 3px solid ' + color + '; ...' }, line); } ``` 📋 Features Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ Real-time system log viewing (logread integration) ✅ Filter by log level (all, errors, warnings, info) ✅ Search functionality (filter text in logs) ✅ Adjustable line count (50, 100, 200, 500, 1000 lines) ✅ Color-coded log levels for easy identification ✅ Refresh button to reload logs ✅ Download logs as .txt file ✅ Statistics: total lines, errors count, warnings count ✅ Beautiful empty state when no logs match 🚀 Deployment ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ✅ Updated RPCD backend on router ✅ Updated frontend logs.js on router ✅ Permissions fixed (755 for RPCD, 644 for JS) ✅ Services restarted (rpcd, uhttpd) ✅ Tested and working Test URL: https://192.168.8.191/cgi-bin/luci/admin/secubox/system/system-hub/logs Files Modified: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Backend: * luci-app-system-hub/root/usr/libexec/rpcd/luci.system-hub - Fixed get_logs() to return real logs (+12 lines refactor) Frontend: * luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/logs.js - Added renderLogLine() for color-coded display (+27 lines) - Enhanced updateLogDisplay() with empty state (+18 lines) - JetBrains Mono font integration 🎯 User Experience ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Before: ❌ Empty log viewer (backend returned []) ❌ Plain text display ❌ No visual distinction between log levels After: ✅ Real system logs displayed ✅ Color-coded by severity level ✅ Easy to spot errors and warnings ✅ Professional terminal-like appearance ✅ Fully functional filters and search 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../resources/view/system-hub/logs.js | 57 +++++++++++++++++-- .../root/usr/libexec/rpcd/luci.system-hub | 26 ++++++--- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/logs.js b/luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/logs.js index 141bf175..06dba99b 100644 --- a/luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/logs.js +++ b/luci-app-system-hub/htdocs/luci-static/resources/view/system-hub/logs.js @@ -181,13 +181,30 @@ return view.extend({ if (!container) return; var filtered = this.getFilteredLogs(); - var logsText = filtered.length > 0 ? filtered.join('\n') : 'No logs available'; - dom.content(container, [ - E('pre', { - 'style': 'background: var(--sh-bg-tertiary); color: var(--sh-text-primary); padding: 20px; overflow: auto; max-height: 600px; font-size: 12px; font-family: "Courier New", monospace; border-radius: 0; margin: 0; line-height: 1.5;' - }, logsText) - ]); + if (filtered.length === 0) { + dom.content(container, [ + E('div', { + 'style': 'padding: 60px 20px; text-align: center; color: var(--sh-text-secondary);' + }, [ + E('div', { 'style': 'font-size: 48px; margin-bottom: 16px;' }, '📋'), + E('div', { 'style': 'font-size: 16px; font-weight: 500;' }, 'No logs available'), + E('div', { 'style': 'font-size: 14px; margin-top: 8px;' }, + this.searchQuery ? 'Try a different search query' : 'System logs will appear here') + ]) + ]); + } else { + // Render colored log lines + var logLines = filtered.map(function(line) { + return this.renderLogLine(line); + }.bind(this)); + + dom.content(container, [ + E('div', { + 'style': 'background: var(--sh-bg-tertiary); padding: 20px; overflow: auto; max-height: 600px; font-size: 12px; font-family: "JetBrains Mono", "Courier New", monospace; line-height: 1.6;' + }, logLines) + ]); + } // Update badge var badge = document.querySelector('.sh-card-badge'); @@ -196,6 +213,34 @@ return view.extend({ } }, + renderLogLine: function(line) { + var lineLower = line.toLowerCase(); + var color = 'var(--sh-text-primary)'; + var bgColor = 'transparent'; + + // Detect log level and apply color + if (lineLower.includes('error') || lineLower.includes('err') || lineLower.includes('fatal') || lineLower.includes('crit')) { + color = '#ef4444'; // Red for errors + bgColor = 'rgba(239, 68, 68, 0.1)'; + } else if (lineLower.includes('warn') || lineLower.includes('warning')) { + color = '#f59e0b'; // Orange for warnings + bgColor = 'rgba(245, 158, 11, 0.1)'; + } else if (lineLower.includes('info') || lineLower.includes('notice')) { + color = '#3b82f6'; // Blue for info + bgColor = 'rgba(59, 130, 246, 0.1)'; + } else if (lineLower.includes('debug')) { + color = '#8b5cf6'; // Purple for debug + bgColor = 'rgba(139, 92, 246, 0.1)'; + } else if (lineLower.includes('success') || lineLower.includes('ok')) { + color = '#22c55e'; // Green for success + bgColor = 'rgba(34, 197, 94, 0.1)'; + } + + return E('div', { + 'style': 'padding: 4px 8px; margin: 0; color: ' + color + '; background: ' + bgColor + '; border-left: 3px solid ' + color + '; margin-bottom: 2px; border-radius: 2px;' + }, line); + }, + getFilteredLogs: function() { return this.logs.filter(function(line) { var lineLower = line.toLowerCase(); diff --git a/luci-app-system-hub/root/usr/libexec/rpcd/luci.system-hub b/luci-app-system-hub/root/usr/libexec/rpcd/luci.system-hub index 987b7ca2..03dd0290 100755 --- a/luci-app-system-hub/root/usr/libexec/rpcd/luci.system-hub +++ b/luci-app-system-hub/root/usr/libexec/rpcd/luci.system-hub @@ -388,20 +388,28 @@ get_logs() { json_get_var filter filter "" json_cleanup + # Get logs into a temporary file to avoid subshell issues + local tmpfile="/tmp/syslog-$$" + + if [ -n "$filter" ]; then + logread | tail -n "$lines" | grep -i "$filter" > "$tmpfile" + else + logread | tail -n "$lines" > "$tmpfile" + fi + json_init json_add_array "logs" - if [ -n "$filter" ]; then - logread | tail -n "$lines" | grep -i "$filter" | while IFS= read -r line; do - json_add_string "" "$line" - done - else - logread | tail -n "$lines" | while IFS= read -r line; do - json_add_string "" "$line" - done - fi + # Read from file line by line + while IFS= read -r line; do + json_add_string "" "$line" + done < "$tmpfile" json_close_array + + # Cleanup + rm -f "$tmpfile" + json_dump }