Last active
November 17, 2025 05:16
-
-
Save santakdalai90/e0893c86928a230901acf0e6f28c5e93 to your computer and use it in GitHub Desktop.
JSON Log Viewer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Filterable JSON Log Viewer</title> | |
| <style> | |
| /* --- General Reset & Structure --- */ | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background-color: #f0f2f5; /* Very light, soft background */ | |
| color: #333; | |
| line-height: 1.6; | |
| } | |
| h1 { | |
| color: #1e3a8a; /* Dark blue primary color */ | |
| margin-bottom: 5px; | |
| } | |
| /* --- Main Containers & Input Area --- */ | |
| #log-header { | |
| background-color: #ffffff; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
| margin-bottom: 20px; | |
| } | |
| #controls { | |
| background-color: #ffffff; | |
| padding: 15px 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
| margin-bottom: 20px; | |
| } | |
| /* --- Form Elements --- */ | |
| .control-group { | |
| margin-right: 25px; | |
| display: inline-block; | |
| } | |
| input[type="text"], select, #fileInput { | |
| padding: 8px 12px; | |
| border: 1px solid #ccc; | |
| border-radius: 4px; | |
| transition: border-color 0.3s; | |
| } | |
| input[type="text"]:focus, select:focus { | |
| border-color: #3b82f6; /* Blue focus highlight */ | |
| outline: none; | |
| } | |
| button { | |
| background-color: #3b82f6; /* Primary blue button */ | |
| color: white; | |
| border: none; | |
| padding: 8px 15px; | |
| cursor: pointer; | |
| border-radius: 4px; | |
| font-weight: 600; | |
| transition: background-color 0.3s; | |
| } | |
| button:hover { | |
| background-color: #2563eb; | |
| } | |
| hr { | |
| border: none; | |
| border-top: 1px solid #eee; | |
| margin: 15px 0; | |
| } | |
| /* --- Table Styles --- */ | |
| #logTableContainer { | |
| margin-top: 20px; | |
| overflow-x: auto; | |
| background-color: #ffffff; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
| } | |
| #logTable { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.9em; | |
| } | |
| #logTable th, #logTable td { | |
| padding: 12px 15px; | |
| text-align: left; | |
| border-bottom: 1px solid #f0f0f0; /* Very light border */ | |
| } | |
| #logTable th { | |
| background-color: #eef2ff; /* Very light blue header background */ | |
| color: #1e3a8a; | |
| cursor: pointer; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| font-size: 0.85em; | |
| } | |
| #logTable tr:hover { | |
| background-color: #f7f9fc; | |
| } | |
| /* --- Log Level Tag Styling --- */ | |
| .level-tag { | |
| padding: 4px 10px; | |
| border-radius: 16px; | |
| font-size: 0.75em; | |
| font-weight: 700; | |
| color: white; | |
| text-transform: uppercase; | |
| display: inline-block; | |
| } | |
| .level-info { background-color: #3b82f6; } /* Blue */ | |
| .level-warn { background-color: #f59e0b; color: #333;} /* Amber/Yellow */ | |
| .level-error { background-color: #ef4444; } /* Red */ | |
| .level-debug { background-color: #9ca3af; } /* Grey */ | |
| .level-unknown { background-color: #4b5563; } /* Dark Grey */ | |
| /* --- Message Column Styles --- */ | |
| .message-cell { | |
| white-space: pre-wrap; | |
| font-family: monospace; | |
| font-size: 0.8em; | |
| color: #555; | |
| } | |
| .error-message { | |
| color: #ef4444; | |
| font-weight: bold; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="log-header"> | |
| <h1>Filterable JSON Log Viewer 📊</h1> | |
| <p>Upload a file where each line is a single JSON log entry to view and filter logs below.</p> | |
| <input type="file" id="fileInput" accept=".log,.txt,text/plain" /> | |
| <button onclick="processFile()">Process Log File</button> | |
| </div> | |
| <div id="controls" style="display: none;"> | |
| <div class="control-group"> | |
| <label for="levelFilter">Filter by Level:</label> | |
| <select id="levelFilter" onchange="applyFilters()"> | |
| <option value="all">All Levels</option> | |
| </select> | |
| </div> | |
| <div class="control-group"> | |
| <label for="searchBox">Search Message:</label> | |
| <input type="text" id="searchBox" placeholder="Enter text to search" onkeyup="applyFilters()"> | |
| </div> | |
| </div> | |
| <div id="logTableContainer"> | |
| <p>Upload a file to view logs here.</p> | |
| </div> | |
| <script> | |
| // Store all parsed log entries globally for easy filtering/searching | |
| let allLogs = []; | |
| function processFile() { | |
| const inputElement = document.getElementById('fileInput'); | |
| const outputElement = document.getElementById('logTableContainer'); | |
| const file = inputElement.files[0]; | |
| outputElement.innerHTML = ''; // Clear previous output | |
| allLogs = []; // Reset logs | |
| if (!file) { | |
| outputElement.innerHTML = '<p class="error-message">Please select a file to process.</p>'; | |
| return; | |
| } | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const fileContent = e.target.result; | |
| const logLines = fileContent.split('\n'); | |
| logLines.forEach((line, index) => { | |
| const trimmedLine = line.trim(); | |
| if (trimmedLine === '') return; | |
| try { | |
| const logObject = JSON.parse(trimmedLine); | |
| allLogs.push({ | |
| time: logObject.time || 'N/A', | |
| level: (logObject.level || 'unknown').toLowerCase(), | |
| message: trimmedLine, | |
| originalIndex: index | |
| }); | |
| } catch (error) { | |
| allLogs.push({ | |
| time: `Line ${index + 1}`, | |
| level: 'unknown', | |
| message: `Error parsing JSON: ${trimmedLine}`, | |
| originalIndex: index | |
| }); | |
| } | |
| }); | |
| buildTable(allLogs); | |
| populateLevelFilter(); | |
| document.getElementById('controls').style.display = 'block'; | |
| }; | |
| reader.readAsText(file); | |
| } | |
| function buildTable(logs) { | |
| const container = document.getElementById('logTableContainer'); | |
| if (logs.length === 0) { | |
| container.innerHTML = '<p>No log entries found or processed.</p>'; | |
| return; | |
| } | |
| let tableHTML = ` | |
| <table id="logTable"> | |
| <thead> | |
| <tr> | |
| <th onclick="sortTable(0)">Time</th> | |
| <th onclick="sortTable(1)">Level</th> | |
| <th onclick="sortTable(2)">Message</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| `; | |
| logs.forEach(log => { | |
| const levelClass = `level-${log.level}`; | |
| tableHTML += ` | |
| <tr> | |
| <td>${log.time}</td> | |
| <td><span class="level-tag ${levelClass}">${log.level}</span></td> | |
| <td class="message-cell">${log.message}</td> | |
| </tr> | |
| `; | |
| }); | |
| tableHTML += ` | |
| </tbody> | |
| </table> | |
| `; | |
| container.innerHTML = tableHTML; | |
| } | |
| function populateLevelFilter() { | |
| const filter = document.getElementById('levelFilter'); | |
| filter.innerHTML = '<option value="all">All Levels</option>'; | |
| const uniqueLevels = [...new Set(allLogs.map(log => log.level))].sort(); | |
| uniqueLevels.forEach(level => { | |
| const option = document.createElement('option'); | |
| option.value = level; | |
| option.textContent = level.charAt(0).toUpperCase() + level.slice(1); | |
| filter.appendChild(option); | |
| }); | |
| } | |
| function applyFilters() { | |
| const levelFilterValue = document.getElementById('levelFilter').value; | |
| const searchBoxValue = document.getElementById('searchBox').value.toLowerCase(); | |
| const filteredLogs = allLogs.filter(log => { | |
| const levelMatch = (levelFilterValue === 'all' || log.level === levelFilterValue); | |
| const searchMatch = log.message.toLowerCase().includes(searchBoxValue); | |
| return levelMatch && searchMatch; | |
| }); | |
| buildTable(filteredLogs); | |
| } | |
| function sortTable(columnIndex) { | |
| const tbody = document.getElementById('logTable').querySelector('tbody'); | |
| const rows = Array.from(tbody.querySelectorAll('tr')); | |
| const isAscending = tbody.dataset.sortOrder !== 'asc'; | |
| tbody.dataset.sortOrder = isAscending ? 'asc' : 'desc'; | |
| rows.sort((rowA, rowB) => { | |
| const cellA = rowA.cells[columnIndex].textContent.toLowerCase(); | |
| const cellB = rowB.cells[columnIndex].textContent.toLowerCase(); | |
| let comparison = 0; | |
| if (cellA > cellB) { | |
| comparison = 1; | |
| } else if (cellA < cellB) { | |
| comparison = -1; | |
| } | |
| return isAscending ? comparison : comparison * -1; | |
| }); | |
| rows.forEach(row => tbody.appendChild(row)); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment