This adds a comprehensive web-based log viewer that provides similar functionality to the mobile log viewer, allowing analysis of log files from customer support requests. Features: - Upload and parse ZIP files containing daily log files - Advanced filtering by log level, logger name, process, and timeline - Text search with wildcard support (logger:Service*) - Interactive analytics with click-to-filter charts - Modern UI using Ente's design system and Material-UI components - Infinite scroll for performance with large log files - Export functionality for filtered results - Responsive design for desktop and mobile Technical highlights: - Client-side ZIP processing with JSZip - Efficient log parsing supporting Ente's super_logging format - Real-time filtering with optimized algorithms - Memory-efficient rendering with virtual scrolling - CSS custom properties for theming consistency 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
218 lines
9.5 KiB
HTML
218 lines
9.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Ente Log Viewer</title>
|
|
|
|
<!-- Material-UI CSS -->
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mui/material@latest/dist/index.css" />
|
|
|
|
<!-- Ente theme -->
|
|
<link rel="stylesheet" href="ente-theme.css">
|
|
|
|
<!-- Custom styles -->
|
|
<link rel="stylesheet" href="styles.css">
|
|
</head>
|
|
<body>
|
|
<div class="app">
|
|
<!-- Header -->
|
|
<header class="header">
|
|
<div class="header-content">
|
|
<h1>📋 Ente Log Viewer</h1>
|
|
<div class="header-actions">
|
|
<button id="filter-btn" class="mui-icon-btn" title="Filter logs">
|
|
<span class="material-icons">filter_list</span>
|
|
<span class="filter-count" id="filter-count" style="display: none;"></span>
|
|
</button>
|
|
<button id="sort-btn" class="mui-icon-btn" title="Sort order">
|
|
<span class="material-icons">arrow_downward</span>
|
|
</button>
|
|
<div class="dropdown">
|
|
<button class="mui-icon-btn dropdown-toggle" title="More actions">
|
|
<span class="material-icons">more_vert</span>
|
|
</button>
|
|
<div class="dropdown-menu mui-menu">
|
|
<button id="analytics-btn" class="dropdown-item mui-menu-item">
|
|
<span class="material-icons">analytics</span>
|
|
<span>Analytics</span>
|
|
</button>
|
|
<button id="export-btn" class="dropdown-item mui-menu-item">
|
|
<span class="material-icons">download</span>
|
|
<span>Export Logs</span>
|
|
</button>
|
|
<button id="clear-btn" class="dropdown-item mui-menu-item danger">
|
|
<span class="material-icons">delete</span>
|
|
<span>Clear Logs</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Upload Section -->
|
|
<div class="upload-section" id="upload-section">
|
|
<div class="upload-area" id="upload-area">
|
|
<div class="upload-content">
|
|
<div class="upload-icon">📁</div>
|
|
<h2>Upload Log Files</h2>
|
|
<p>Drag and drop a zip file containing log files, or click to browse</p>
|
|
<input type="file" id="file-input" accept=".zip" hidden>
|
|
<button id="browse-btn" class="primary-btn">Choose ZIP File</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="main-content" id="main-content" style="display: none;">
|
|
<!-- Search Bar -->
|
|
<div class="search-section">
|
|
<div class="search-bar mui-search-container">
|
|
<div class="mui-textfield">
|
|
<input type="text" id="search-input" placeholder="Search logs... (try 'logger:SyncService' or text search)" class="mui-input" />
|
|
<span class="mui-search-icon material-icons">search</span>
|
|
<button id="clear-search" class="mui-clear-btn" style="display: none;">
|
|
<span class="material-icons">clear</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Timeline Filter -->
|
|
<div class="timeline-section" id="timeline-section" style="display: none;">
|
|
<div class="timeline-header">
|
|
<span class="material-icons">timeline</span>
|
|
<span>Timeline Filter</span>
|
|
<button id="timeline-toggle" class="mui-icon-btn timeline-btn">
|
|
<span class="material-icons">timeline</span>
|
|
</button>
|
|
</div>
|
|
<div class="timeline-controls" id="timeline-controls" style="display: none;">
|
|
<div class="timeline-range">
|
|
<div class="mui-textfield">
|
|
<input type="datetime-local" id="start-time" class="mui-input" />
|
|
</div>
|
|
<span class="range-separator">to</span>
|
|
<div class="mui-textfield">
|
|
<input type="datetime-local" id="end-time" class="mui-input" />
|
|
</div>
|
|
<button id="reset-timeline" class="mui-button secondary">
|
|
<span class="material-icons">refresh</span>
|
|
<span>Reset</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Filters -->
|
|
<div class="active-filters" id="active-filters" style="display: none;">
|
|
<div class="filter-chips" id="filter-chips"></div>
|
|
</div>
|
|
|
|
<!-- Log Stats -->
|
|
<div class="log-stats" id="log-stats">
|
|
<span id="log-count">0 logs loaded</span>
|
|
<span id="filtered-count"></span>
|
|
</div>
|
|
|
|
<!-- Log List -->
|
|
<div class="log-list-container">
|
|
<div class="log-list" id="log-list">
|
|
<!-- Log entries will be populated here -->
|
|
</div>
|
|
<div class="loading" id="loading" style="display: none;">Loading...</div>
|
|
<div class="load-more" id="load-more" style="display: none;">
|
|
<button class="secondary-btn">Load More</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Dialog -->
|
|
<div class="dialog-overlay" id="filter-dialog" style="display: none;">
|
|
<div class="dialog">
|
|
<div class="dialog-header">
|
|
<h2>Filter Logs</h2>
|
|
<button class="close-btn" id="close-filter">✕</button>
|
|
</div>
|
|
<div class="dialog-content">
|
|
<!-- Log Levels -->
|
|
<div class="filter-section">
|
|
<h3>Log Levels</h3>
|
|
<div class="level-chips" id="level-chips">
|
|
<!-- Level chips will be populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Process -->
|
|
<div class="filter-section">
|
|
<h3>Process</h3>
|
|
<div class="process-list" id="process-list">
|
|
<!-- Process checkboxes will be populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loggers -->
|
|
<div class="filter-section">
|
|
<h3>Loggers</h3>
|
|
<div class="logger-list" id="logger-list">
|
|
<!-- Logger checkboxes will be populated here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="dialog-actions">
|
|
<button id="clear-filters" class="secondary-btn">Clear All</button>
|
|
<button id="cancel-filter" class="secondary-btn">Cancel</button>
|
|
<button id="apply-filters" class="primary-btn">Apply</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Analytics Dialog -->
|
|
<div class="dialog-overlay" id="analytics-dialog" style="display: none;">
|
|
<div class="dialog">
|
|
<div class="dialog-header">
|
|
<h2>Logger Analytics</h2>
|
|
<button class="close-btn" id="close-analytics">✕</button>
|
|
</div>
|
|
<div class="dialog-content">
|
|
<div id="analytics-content">
|
|
<!-- Analytics charts will be populated here -->
|
|
</div>
|
|
</div>
|
|
<div class="dialog-actions">
|
|
<button id="close-analytics-btn" class="secondary-btn">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Log Detail Dialog -->
|
|
<div class="dialog-overlay" id="detail-dialog" style="display: none;">
|
|
<div class="dialog large">
|
|
<div class="dialog-header">
|
|
<h2>Log Details</h2>
|
|
<button class="close-btn" id="close-detail">✕</button>
|
|
</div>
|
|
<div class="dialog-content">
|
|
<div id="detail-content">
|
|
<!-- Log details will be populated here -->
|
|
</div>
|
|
</div>
|
|
<div class="dialog-actions">
|
|
<button id="copy-log" class="secondary-btn">Copy</button>
|
|
<button id="close-detail-btn" class="secondary-btn">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Material-UI JavaScript -->
|
|
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
<script src="https://unpkg.com/@mui/material@latest/umd/material-ui.production.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
|
<script src="script.js"></script>
|
|
</body>
|
|
</html> |