website-htmx-rustelo-code/site/public/logs.html
2026-07-10 03:44:13 +01:00

329 lines
9.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Browser Console Logs Viewer</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Monaco', 'Courier New', monospace;
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
margin-bottom: 20px;
}
h1 {
font-size: 24px;
margin-bottom: 10px;
color: #4ec9b0;
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
button {
padding: 8px 16px;
background: #0e639c;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #1177bb;
}
.filter-buttons button {
padding: 6px 12px;
font-size: 12px;
background: #2d2d2d;
color: #d4d4d4;
border: 1px solid #555;
}
.filter-buttons button.active {
background: #0e639c;
color: white;
border-color: #0e639c;
}
.stats {
background: #252526;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
font-size: 13px;
border-left: 4px solid #4ec9b0;
}
.stats-item {
display: inline-block;
margin-right: 20px;
}
#logs-container {
background: #1e1e1e;
border: 1px solid #3e3e42;
border-radius: 4px;
overflow: auto;
max-height: 600px;
padding: 0;
}
.log-entry {
padding: 10px 15px;
border-bottom: 1px solid #3e3e42;
font-size: 12px;
line-height: 1.4;
display: flex;
gap: 10px;
}
.log-entry:hover {
background: #2d2d30;
}
.log-timestamp {
color: #858585;
flex-shrink: 0;
min-width: 200px;
}
.log-level {
flex-shrink: 0;
min-width: 60px;
font-weight: bold;
}
.log-level.LOG {
color: #6a9955;
}
.log-level.ERROR {
color: #f48771;
}
.log-level.WARN {
color: #dcdcaa;
}
.log-level.INFO {
color: #4ec9b0;
}
.log-level.DEBUG {
color: #9cdcfe;
}
.log-message {
flex: 1;
word-break: break-word;
white-space: pre-wrap;
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #858585;
}
.footer {
margin-top: 20px;
text-align: center;
font-size: 12px;
color: #858585;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🔍 Browser Console Logs</h1>
<p>Real-time capture of browser console output</p>
</header>
<div class="stats" id="stats">
<div class="stats-item">Total Logs: <strong id="total-count">0</strong></div>
<div class="stats-item">Errors: <strong id="error-count" style="color: #f48771;">0</strong></div>
<div class="stats-item">Warnings: <strong id="warn-count" style="color: #dcdcaa;">0</strong></div>
<div class="stats-item">Last Updated: <strong id="last-update">Never</strong></div>
</div>
<div class="controls">
<button onclick="reloadLogs()">🔄 Reload</button>
<button onclick="clearLogs()">Clear All</button>
<button onclick="toggleAutoRefresh()" id="auto-refresh-btn">Auto-Refresh: OFF</button>
<div class="filter-buttons" style="display: flex; gap: 5px;">
<button class="active" onclick="filterLogs('all')" data-filter="all">All</button>
<button onclick="filterLogs('ERROR')" data-filter="ERROR">Errors</button>
<button onclick="filterLogs('WARN')" data-filter="WARN">Warnings</button>
<button onclick="filterLogs('LOG')" data-filter="LOG">Logs</button>
</div>
</div>
<div id="logs-container">
<div class="empty-state">
<p>Waiting for console output...</p>
<p style="font-size: 11px; margin-top: 10px;">To capture logs, visit another page on this site and use console.log(), console.error(), etc.</p>
<button onclick="testLog()" style="margin-top: 15px; padding: 6px 12px; font-size: 12px; background: #2d2d2d; color: #d4d4d4; border: 1px solid #555; border-radius: 4px; cursor: pointer;">
📝 Add Test Log
</button>
</div>
</div>
<div class="footer">
<p>This page captures all browser console output in real-time. Refresh the page to continue monitoring.</p>
</div>
</div>
<script>
let autoRefresh = false;
let currentFilter = 'all';
let allLogs = [];
function loadLogsFromStorage() {
try {
const stored = localStorage.getItem('console_logs');
if (stored) {
allLogs = JSON.parse(stored);
return true;
}
} catch (e) {
console.error('Failed to load logs from storage:', e);
}
return false;
}
function saveLogsToStorage() {
try {
localStorage.setItem('console_logs', JSON.stringify(allLogs));
} catch (e) {
console.error('Failed to save logs to storage:', e);
}
}
function fetchLogs() {
// Load from localStorage (primary source when using console-logger.js)
const loaded = loadLogsFromStorage();
renderLogs();
updateStats();
updateTimestamp();
}
function renderLogs() {
const container = document.getElementById('logs-container');
const filteredLogs = allLogs.filter(log => {
return currentFilter === 'all' || log.level === currentFilter;
});
if (filteredLogs.length === 0) {
container.innerHTML = '<div class="empty-state"><p>No logs match the current filter</p></div>';
return;
}
container.innerHTML = filteredLogs.map(log => `
<div class="log-entry">
<span class="log-timestamp">${new Date(log.timestamp).toLocaleTimeString()}</span>
<span class="log-level ${log.level}">${log.level}</span>
<span class="log-message">${escapeHtml(log.message)}</span>
</div>
`).join('');
// Auto-scroll to bottom
container.scrollTop = container.scrollHeight;
}
function updateStats() {
document.getElementById('total-count').textContent = allLogs.length;
document.getElementById('error-count').textContent = allLogs.filter(l => l.level === 'ERROR').length;
document.getElementById('warn-count').textContent = allLogs.filter(l => l.level === 'WARN').length;
}
function updateTimestamp() {
const now = new Date();
document.getElementById('last-update').textContent = now.toLocaleTimeString();
}
function clearLogs() {
if (confirm('Clear all logs?')) {
allLogs = [];
renderLogs();
updateStats();
}
}
function toggleAutoRefresh() {
autoRefresh = !autoRefresh;
const btn = document.getElementById('auto-refresh-btn');
btn.textContent = `Auto-Refresh: ${autoRefresh ? 'ON' : 'OFF'}`;
btn.style.background = autoRefresh ? '#0e639c' : '#2d2d2d';
}
function reloadLogs() {
fetchLogs();
}
function filterLogs(level) {
currentFilter = level;
document.querySelectorAll('.filter-buttons button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.filter === level);
});
renderLogs();
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function testLog() {
console.log('📝 Test log entry - ' + new Date().toLocaleTimeString());
setTimeout(() => {
fetchLogs();
}, 100);
}
// Initial load
fetchLogs();
// Auto-refresh every 1 second if enabled
setInterval(() => {
if (autoRefresh) {
fetchLogs();
}
}, 1000);
// Inject console-logger.js into all frames on this domain
// This allows logs from other pages to be captured when accessed via logs.html
if (window.self === window.top) { // Only in main frame, not iframes
const script = document.createElement('script');
script.src = '/public/console-logger.js';
script.async = true;
document.head.appendChild(script);
}
</script>
</body>
</html>