b2in/public/_cabinet/view-logs.php
2026-01-23 17:33:10 +01:00

319 lines
9.5 KiB
PHP

<?php
// view-logs.php - Einfacher Log-Viewer für Cabinet Digital Signage
// ACHTUNG: In Produktion mit Passwortschutz versehen!
$logsDir = __DIR__ . '/logs';
$selectedFile = $_GET['file'] ?? null;
$logLevel = $_GET['level'] ?? 'all';
$lines = $_GET['lines'] ?? 100;
// Verfügbare Log-Dateien
$logFiles = glob($logsDir . '/*.log');
rsort($logFiles); // Neueste zuerst
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cabinet Logs Viewer</title>
<style>
body {
font-family: 'Courier New', monospace;
margin: 0;
padding: 20px;
background-color: #1e1e1e;
color: #d4d4d4;
}
.header {
background-color: #252526;
padding: 20px;
border-radius: 5px;
margin-bottom: 20px;
}
.header h1 {
margin: 0 0 15px 0;
color: #009FE3;
}
.controls {
display: flex;
gap: 15px;
flex-wrap: wrap;
align-items: center;
}
select,
button,
input {
padding: 8px 12px;
border-radius: 4px;
border: 1px solid #3e3e42;
background-color: #2d2d30;
color: #d4d4d4;
font-size: 14px;
}
button {
cursor: pointer;
background-color: #009FE3;
border: none;
color: white;
font-weight: bold;
}
button:hover {
background-color: #0082bd;
}
.log-container {
background-color: #252526;
padding: 20px;
border-radius: 5px;
overflow-x: auto;
max-height: 80vh;
overflow-y: auto;
}
.log-line {
margin-bottom: 10px;
padding: 5px;
border-left: 3px solid transparent;
}
.log-line.FATAL {
border-left-color: #f44336;
background-color: rgba(244, 67, 54, 0.1);
}
.log-line.ERROR {
border-left-color: #ff9800;
background-color: rgba(255, 152, 0, 0.1);
}
.log-line.WARNING {
border-left-color: #ffeb3b;
background-color: rgba(255, 235, 59, 0.1);
}
.log-line.INFO {
border-left-color: #4caf50;
}
.level {
display: inline-block;
padding: 2px 8px;
border-radius: 3px;
font-weight: bold;
margin-right: 10px;
}
.level.FATAL {
background-color: #f44336;
color: white;
}
.level.ERROR {
background-color: #ff9800;
color: white;
}
.level.WARNING {
background-color: #ffeb3b;
color: #333;
}
.level.INFO {
background-color: #4caf50;
color: white;
}
.timestamp {
color: #858585;
margin-right: 10px;
}
.message {
color: #d4d4d4;
}
.context {
margin-left: 30px;
color: #9cdcfe;
font-size: 0.9em;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background-color: #252526;
padding: 15px;
border-radius: 5px;
text-align: center;
}
.stat-value {
font-size: 2em;
font-weight: bold;
color: #009FE3;
}
.stat-label {
color: #858585;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="header">
<h1>📊 Cabinet Digital Signage - Log Viewer</h1>
<div class="controls">
<select name="file" id="file-select" onchange="location.href='?file=' + this.value + '&lines=<?= $lines ?>'">
<option value="">-- Wähle Log-Datei --</option>
<?php foreach ($logFiles as $file):
$filename = basename($file);
$selected = ($selectedFile === $file) ? 'selected' : '';
?>
<option value="<?= htmlspecialchars($file) ?>" <?= $selected ?>>
<?= htmlspecialchars($filename) ?> (<?= formatBytes(filesize($file)) ?>)
</option>
<?php endforeach; ?>
</select>
<select name="lines" id="lines-select" onchange="location.href='?file=<?= urlencode($selectedFile) ?>&lines=' + this.value">
<option value="50" <?= $lines == 50 ? 'selected' : '' ?>>50 Zeilen</option>
<option value="100" <?= $lines == 100 ? 'selected' : '' ?>>100 Zeilen</option>
<option value="500" <?= $lines == 500 ? 'selected' : '' ?>>500 Zeilen</option>
<option value="1000" <?= $lines == 1000 ? 'selected' : '' ?>>1000 Zeilen</option>
<option value="-1" <?= $lines == -1 ? 'selected' : '' ?>>Alle</option>
</select>
<button onclick="location.reload()">🔄 Aktualisieren</button>
<button onclick="autoRefresh()">⏱️ Auto-Refresh</button>
</div>
</div>
<?php
if ($selectedFile && file_exists($selectedFile)) {
// Statistiken
$allContent = file_get_contents($selectedFile);
$stats = [
'FATAL' => substr_count($allContent, '[FATAL]'),
'ERROR' => substr_count($allContent, '[ERROR]'),
'WARNING' => substr_count($allContent, '[WARNING]'),
'INFO' => substr_count($allContent, '[INFO]')
];
?>
<div class="stats">
<div class="stat-card">
<div class="stat-value" style="color: #f44336;"><?= $stats['FATAL'] ?></div>
<div class="stat-label">FATAL Errors</div>
</div>
<div class="stat-card">
<div class="stat-value" style="color: #ff9800;"><?= $stats['ERROR'] ?></div>
<div class="stat-label">Errors</div>
</div>
<div class="stat-card">
<div class="stat-value" style="color: #ffeb3b;"><?= $stats['WARNING'] ?></div>
<div class="stat-label">Warnings</div>
</div>
<div class="stat-card">
<div class="stat-value" style="color: #4caf50;"><?= $stats['INFO'] ?></div>
<div class="stat-label">Info Messages</div>
</div>
</div>
<div class="log-container">
<?php
// Log-Datei einlesen
$logLines = file($selectedFile, FILE_IGNORE_NEW_LINES);
// Nur die letzten X Zeilen anzeigen
if ($lines > 0) {
$logLines = array_slice($logLines, -$lines);
}
$currentEntry = '';
foreach ($logLines as $line) {
// Erkennen von Log-Level
if (preg_match('/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]\s+\[([A-Z]+)\s*\]\s+(.+)/', $line, $matches)) {
if ($currentEntry) {
echo "</div>";
}
$timestamp = $matches[1];
$level = trim($matches[2]);
$message = htmlspecialchars($matches[3]);
echo "<div class='log-line {$level}'>";
echo "<span class='timestamp'>{$timestamp}</span>";
echo "<span class='level {$level}'>{$level}</span>";
echo "<span class='message'>{$message}</span>";
$currentEntry = $level;
} else {
// Kontext-Zeilen
echo "<div class='context'>" . htmlspecialchars($line) . "</div>";
}
}
if ($currentEntry) {
echo "</div>";
}
?>
</div>
<?php } else { ?>
<div class="log-container">
<p>Bitte wähle eine Log-Datei aus dem Dropdown-Menü.</p>
<?php if (empty($logFiles)): ?>
<p style="color: #ff9800;">⚠️ Keine Log-Dateien gefunden. Stelle sicher, dass das Logging aktiv ist und das Verzeichnis 'logs/' existiert.</p>
<?php endif; ?>
</div>
<?php } ?>
<script>
let autoRefreshInterval = null;
function autoRefresh() {
if (autoRefreshInterval) {
clearInterval(autoRefreshInterval);
autoRefreshInterval = null;
alert('Auto-Refresh deaktiviert');
} else {
autoRefreshInterval = setInterval(() => {
location.reload();
}, 10000); // Alle 10 Sekunden
alert('Auto-Refresh aktiviert (alle 10 Sekunden)');
}
}
// Automatisch zum Ende scrollen
const logContainer = document.querySelector('.log-container');
if (logContainer) {
logContainer.scrollTop = logContainer.scrollHeight;
}
</script>
</body>
</html>
<?php
function formatBytes($bytes, $precision = 2)
{
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>