319 lines
9.5 KiB
PHP
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];
|
|
}
|
|
?>
|