202 行
9.7 KiB
PHP
202 行
9.7 KiB
PHP
<?php
|
||
require __DIR__ . '/../includes/init.php';
|
||
requireStaff();
|
||
require '_common.php';
|
||
$isAdmin = isAdminStaff();
|
||
$logDir = realpath(__DIR__ . '/../storage/logs') ?: (__DIR__ . '/../storage/logs');
|
||
$msg = '';
|
||
$err = '';
|
||
if (!is_dir($logDir)) {
|
||
@mkdir($logDir, 0755, true);
|
||
}
|
||
$date = trim($_GET['date'] ?? '');
|
||
if ($date === '') {
|
||
$date = date('Y-m-d');
|
||
}
|
||
if (!preg_match('
|
||
$date = date('Y-m-d');
|
||
}
|
||
$logFile = $logDir . DIRECTORY_SEPARATOR . $date . '.log';
|
||
$lines = [];
|
||
$totalLines = 0;
|
||
$pageSize = 100;
|
||
$page = max(1, (int) ($_GET['page'] ?? 1));
|
||
if (file_exists($logFile) && is_readable($logFile)) {
|
||
$content = file_get_contents($logFile);
|
||
$allLines = explode("\n", rtrim($content));
|
||
$totalLines = count($allLines);
|
||
$allLines = array_reverse($allLines);
|
||
$offset = ($page - 1) * $pageSize;
|
||
$lines = array_slice($allLines, $offset, $pageSize);
|
||
} elseif (file_exists($logFile)) {
|
||
$err = '日志文件不可读,请检查目录权限。';
|
||
}
|
||
$logFiles = glob($logDir . DIRECTORY_SEPARATOR . '*.log');
|
||
$dates = [];
|
||
foreach ($logFiles as $f) {
|
||
$n = basename($f);
|
||
if (preg_match('
|
||
$dates[] = $m[1];
|
||
}
|
||
}
|
||
rsort($dates);
|
||
$totalPages = max(1, ceil($totalLines / $pageSize));
|
||
adminHeader('系统日志', 'logs');
|
||
$tab = trim($_GET['tab'] ?? 'sys');
|
||
if ($tab === 'visit') {
|
||
$vPage = max(1, (int) ($_GET['vpage'] ?? 1));
|
||
$vSize = 80;
|
||
$vTotal = (int) db()->query('SELECT COUNT(*) FROM ' . tn('visit_logs'))->fetchColumn();
|
||
$stmt = db()->prepare('SELECT v.*, u.username FROM ' . tn('visit_logs') . ' v LEFT JOIN ' . tn('users') . ' u ON u.id = v.user_id ORDER BY v.id DESC LIMIT ? OFFSET ?');
|
||
$stmt->execute([$vSize, ($vPage - 1) * $vSize]);
|
||
$vRows = $stmt->fetchAll();
|
||
$vPages = max(1, ceil($vTotal / $vSize));
|
||
?>
|
||
<h1 class="page-title"><i class="fas fa-scroll"></i> 系统日志</h1>
|
||
<div class="log-tabs">
|
||
<a href="?tab=sys" class="log-tab">运行日志</a>
|
||
<a href="?tab=visit" class="log-tab active">访问日志(IP)</a>
|
||
</div>
|
||
<div class="panel">
|
||
<h3><i class="fas fa-network-wired"></i> 访问日志(访客 IP)<?php if ($vTotal > 0): ?><span class="muted" style="font-weight:400;font-size:.85rem">(共 <?= number_format($vTotal) ?> 条)</span><?php endif; ?></h3>
|
||
<?php if (empty($vRows)): ?>
|
||
<p class="empty">暂无访问记录。访问日志在访客打开任意页面时自动记录。</p>
|
||
<?php else: ?>
|
||
<div class="log-viewer">
|
||
<table class="data-table">
|
||
<thead><tr><th style="width:150px">时间</th><th style="width:130px">IP</th><th style="width:90px">用户</th><th>URI</th><th style="width:120px">UA</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($vRows as $r): ?>
|
||
<tr>
|
||
<td><code style="font-size:.78rem;white-space:nowrap"><?= h(substr($r['created_at'], 0, 19)) ?></code></td>
|
||
<td><code style="font-size:.8rem"><?= h($r['ip']) ?></code></td>
|
||
<td><?= $r['user_id'] ? h($r['username'] ?? ('
|
||
<td style="font-family:monospace;font-size:.82rem"><?= h($r['method']) ?> <?= h($r['uri']) ?></td>
|
||
<td style="font-size:.74rem;color:var(--muted);max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="<?= h($r['ua']) ?>"><?= h(mb_substr($r['ua'], 0, 40)) ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php if ($vPages > 1): ?>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-top:12px;font-size:.88rem">
|
||
<?php if ($vPage > 1): ?><a href="?tab=visit&vpage=<?= $vPage - 1 ?>" class="btn btn-ghost btn-sm">« 上一页</a><?php endif; ?>
|
||
<span class="muted">第 <strong><?= $vPage ?></strong> / <?= $vPages ?> 页</span>
|
||
<?php if ($vPage < $vPages): ?><a href="?tab=visit&vpage=<?= $vPage + 1 ?>" class="btn btn-ghost btn-sm">下一页 »</a><?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
adminFooter();
|
||
return;
|
||
}
|
||
?>
|
||
<h1 class="page-title"><i class="fas fa-scroll"></i> 系统日志</h1>
|
||
<div class="log-tabs">
|
||
<a href="?tab=sys" class="log-tab active">运行日志</a>
|
||
<a href="?tab=visit" class="log-tab">访问日志(IP)</a>
|
||
</div>
|
||
<?php if ($msg): ?><p class="banner-ok"><i class="fas fa-check-circle"></i> <?= h($msg) ?></p><?php endif; ?>
|
||
<?php if ($err): ?><p class="form-err"><?= h($err) ?></p><?php endif; ?>
|
||
<!-- 日期选择 -->
|
||
<div class="panel" style="margin-bottom:18px;">
|
||
<form method="get" action="" style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
|
||
<label style="font-weight:600;margin:0">选择日期:
|
||
<input type="date" name="date" value="<?= h($date) ?>" style="padding:6px 10px;border:1px solid var(--outline);border-radius:8px;font-size:.9rem">
|
||
</label>
|
||
<button type="submit" class="btn btn-ghost btn-sm"><i class="fas fa-search"></i> 查看</button>
|
||
<?php if (!empty($dates)): ?>
|
||
<span class="muted" style="font-size:.82rem">可用日期:
|
||
<?php foreach (array_slice($dates, 0, 14) as $d): ?>
|
||
<a href="?date=<?= h($d) ?>" class="<?= $d === $date ? 'btn btn-sm btn-primary' : 'btn btn-ghost btn-sm' ?>" style="padding:2px 8px;font-size:.78rem"><?= h($d) ?></a>
|
||
<?php endforeach; ?>
|
||
<?php if (count($dates) > 14): ?><span class="muted">... 等 <?= count($dates) ?> 天</span><?php endif; ?>
|
||
</span>
|
||
<?php endif; ?>
|
||
</form>
|
||
</div>
|
||
<div class="panel">
|
||
<h3>
|
||
<i class="fas fa-file-lines"></i> <?= h($date) ?>.log
|
||
<?php if ($totalLines > 0): ?>
|
||
<span class="muted" style="font-weight:400;font-size:.85rem">(共 <?= number_format($totalLines) ?> 条)</span>
|
||
<?php endif; ?>
|
||
</h3>
|
||
<?php if (empty($lines) && empty($err)): ?>
|
||
<p class="empty">当天暂无日志记录。</p>
|
||
<p class="muted" style="font-size:.85rem">
|
||
日志级别:DEBUG / INFO / WARN / ERROR。当前模式:<strong><?= defined('DEBUG') && DEBUG ? 'DEBUG(记录全部)' : 'INFO(及以上)' ?></strong><br>
|
||
可在 config.php 中修改 <code>define('DEBUG', true)</code> 以开启 DEBUG 级别记录。
|
||
</p>
|
||
<?php else: ?>
|
||
<!-- 分页 -->
|
||
<?php if ($totalPages > 1): ?>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;font-size:.88rem">
|
||
<?php if ($page > 1): ?>
|
||
<a href="?date=<?= h($date) ?>&page=<?= $page - 1 ?>" class="btn btn-ghost btn-sm">« 上一页</a>
|
||
<?php endif; ?>
|
||
<span class="muted">第 <strong><?= $page ?></strong> / <?= $totalPages ?> 页</span>
|
||
<?php if ($page < $totalPages): ?>
|
||
<a href="?date=<?= h($date) ?>&page=<?= $page + 1 ?>" class="btn btn-ghost btn-sm">下一页 »</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<div class="log-viewer">
|
||
<table class="data-table">
|
||
<thead><tr><th style="width:160px">时间</th><th style="width:70px">级别</th><th style="width:140px">来源</th><th>内容</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($lines as $line):
|
||
$line = trim($line);
|
||
if ($line === '') continue;
|
||
preg_match('
|
||
$time = $m[1] ?? '';
|
||
$level = $m[2] ?? '';
|
||
$source = $m[3] ?? '';
|
||
$body = $m[4] ?? $line;
|
||
$levelClass = match($level) {
|
||
'DEBUG' => 'log-debug',
|
||
'INFO' => 'log-info',
|
||
'WARN' => 'log-warn',
|
||
'ERROR' => 'log-error',
|
||
default => ''
|
||
};
|
||
?>
|
||
<tr class="<?= $levelClass ?>">
|
||
<td><code style="font-size:.8rem;white-space:nowrap"><?= h($time) ?: '—' ?></code></td>
|
||
<td><span class="badge-status status-<?= strtolower($level) ?>"><?= h($level) ?></span></td>
|
||
<td><code style="font-size:.78rem;color:var(--muted)"><?= h($source) ?: '' ?></code></td>
|
||
<td style="word-break:break-all;font-family:monospace;font-size:.84rem"><?= h($body) ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<!-- 底部分页 -->
|
||
<?php if ($totalPages > 1): ?>
|
||
<div style="display:flex;align-items:center;gap:8px;margin-top:12px;font-size:.88rem">
|
||
<?php if ($page > 1): ?>
|
||
<a href="?date=<?= h($date) ?>&page=<?= $page - 1 ?>" class="btn btn-ghost btn-sm">« 上一页</a>
|
||
<?php endif; ?>
|
||
<span class="muted">第 <strong><?= $page ?></strong> / <?= $totalPages ?> 页</span>
|
||
<?php if ($page < $totalPages): ?>
|
||
<a href="?date=<?= h($date) ?>&page=<?= $page + 1 ?>" class="btn btn-ghost btn-sm">下一页 »</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<style>
|
||
.log-viewer { overflow-x: auto; }
|
||
.log-viewer table { font-size:.85rem; }
|
||
.log-debug { opacity: 0.55; }
|
||
.log-info { }
|
||
.log-warn { background: color-mix(in srgb, var(--warn) 8%, transparent); }
|
||
.log-error { background: color-mix(in srgb, var(--danger) 8%, transparent); }
|
||
.status-debug { background:
|
||
.status-info { background:var(--primary);color:
|
||
.status-warn { background:var(--warn);color:
|
||
.status-error { background:var(--danger);color:
|
||
</style>
|
||
<?php adminFooter(); ?>
|