本文目录导读:

我来为您介绍PHP后台访问日志的完整实现方案:
基础日志类设计
<?php
/**
* 访问日志记录类
*/
class AccessLogger {
private $logPath;
private $logFile;
private $maxFileSize = 10485760; // 10MB
public function __construct($logPath = '/var/logs/php/') {
$this->logPath = $logPath;
if (!file_exists($logPath)) {
mkdir($logPath, 0755, true);
}
}
/**
* 记录访问日志
*/
public function log($data = []) {
$logData = array_merge([
'ip' => $this->getClientIp(),
'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
'url' => $_SERVER['REQUEST_URI'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'referer' => $_SERVER['HTTP_REFERER'] ?? '',
'timestamp' => date('Y-m-d H:i:s'),
'session_id' => session_id() ?: '',
'user_id' => isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 0,
'request_data' => json_encode($data)
], $data);
$logLine = json_encode($logData, JSON_UNESCAPED_UNICODE) . "\n";
$this->writeLog($logLine);
}
/**
* 获取客户端IP
*/
private function getClientIp() {
$ipAddresses = [
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR'
];
foreach ($ipAddresses as $key) {
if (isset($_SERVER[$key])) {
if ($key == 'HTTP_X_FORWARDED_FOR') {
$ipList = explode(',', $_SERVER[$key]);
return trim($ipList[0]);
}
return $_SERVER[$key];
}
}
return 'unknown';
}
/**
* 写入日志(自动轮转)
*/
private function writeLog($content) {
$this->logFile = $this->logPath . date('Y-m-d') . '_access.log';
if (file_exists($this->logFile) && filesize($this->logFile) > $this->maxFileSize) {
$this->rotateLog();
}
file_put_contents($this->logFile, $content, FILE_APPEND | LOCK_EX);
}
/**
* 日志轮转
*/
private function rotateLog() {
$archiveFile = str_replace('.log', '_' . time() . '.log', $this->logFile);
rename($this->logFile, $archiveFile);
file_put_contents($this->logFile, "【" . date('Y-m-d H:i:s') . "】日志轮转\n");
}
/**
* 查询日志
*/
public function queryLogs($filters = [], $page = 1, $perPage = 10) {
$logs = [];
$files = glob($this->logPath . '*_access.log');
if (empty($files)) return ['total' => 0, 'list' => []];
// 按时间倒序排列文件
rsort($files);
foreach ($files as $file) {
$handle = fopen($file, 'r');
if (!$handle) continue;
while (($line = fgets($handle)) !== false) {
$logData = json_decode($line, true);
if (!$logData) continue;
if ($this->matchFilters($logData, $filters)) {
$logs[] = $logData;
}
}
fclose($handle);
if (count($logs) >= $page * $perPage) {
break;
}
}
// 倒序排列
krsort($logs);
$total = count($logs);
$offset = ($page - 1) * $perPage;
$list = array_slice($logs, $offset, $perPage);
return [
'total' => $total,
'page' => $page,
'perPage' => $perPage,
'list' => $list
];
}
/**
* 过滤器匹配
*/
private function matchFilters($logData, $filters) {
foreach ($filters as $key => $value) {
if (isset($logData[$key]) && $logData[$key] == $value) {
return true;
}
}
return empty($filters);
}
}
异常日志记录
<?php
/**
* 异常日志记录类
*/
class ExceptionLogger {
private static $instance = null;
private $logPath;
private function __construct($logPath = '/var/logs/php/error/') {
$this->logPath = $logPath;
if (!is_dir($logPath)) {
mkdir($logPath, 0755, true);
}
}
public static function getInstance($logPath = null) {
if (self::$instance === null) {
self::$instance = new self($logPath);
}
return self::$instance;
}
/**
* 记录异常
*/
public function logException(\Exception $e, $context = []) {
$logData = [
'timestamp' => date('Y-m-d H:i:s'),
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
'context' => json_encode($context),
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'url' => $_SERVER['REQUEST_URI'] ?? ''
];
$logFile = $this->logPath . date('Y-m-d') . '_error.log';
$logLine = json_encode($logData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);
// 可选:发送邮件通知
if (ENVIRONMENT === 'production') {
$this->sendEmailAlert($logData);
}
}
/**
* 记录PHP错误
*/
public function logError($errorNo, $errorStr, $errorFile, $errorLine) {
$logData = [
'timestamp' => date('Y-m-d H:i:s'),
'error_no' => $errorNo,
'error_str' => $errorStr,
'file' => $errorFile,
'line' => $errorLine,
'ip' => $_SERVER['REMOTE_ADDR'] ?? ''
];
$logFile = $this->logPath . date('Y-m-d') . '_error.log';
$logLine = json_encode($logData, JSON_UNESCAPED_UNICODE) . "\n";
error_log($logLine, 3, $logFile);
}
private function sendEmailAlert($data) {
// 实现邮件通知逻辑
$to = ADMIN_EMAIL;
$subject = 'PHP异常通知 - ' . date('Y-m-d H:i:s');
$message = "异常信息:{$data['message']}\n文件:{$data['file']}:{$data['line']}";
mail($to, $subject, $message);
}
}
日志查看器页面
<!DOCTYPE html>
<html>
<head>后台访问日志查询</title>
<style>
body { font-family: Arial, sans-serif; }
.filter-section { background: #f5f5f5; padding: 15px; margin-bottom: 20px; }
.log-table { width: 100%; border-collapse: collapse; }
.log-table th, .log-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
.pagination { margin-top: 20px; }
.detail-modal { display: none; position: fixed; z-index: 1; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.4); }
.modal-content { background-color: #fefefe; margin: 10% auto; padding: 20px; border: 1px solid #888; width: 70%; }
</style>
</head>
<body>
<div class="filter-section">
<h2>访问日志查询</h2>
<form method="GET" action="">
日期:<input type="date" name="date" value="<?php echo $_GET['date'] ?? date('Y-m-d'); ?>">
IP:<input type="text" name="ip" value="<?php echo $_GET['ip'] ?? ''; ?>">
用户ID:<input type="text" name="user_id" value="<?php echo $_GET['user_id'] ?? ''; ?>">
<input type="submit" value="查询">
</form>
</div>
<table class="log-table">
<thead>
<tr>
<th>时间</th>
<th>IP</th>
<th>方法</th>
<th>URL</th>
<th>用户ID</th>
<th>Session</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (!empty($logs)): ?>
<?php foreach ($logs as $log): ?>
<tr>
<td><?php echo $log['timestamp']; ?></td>
<td><?php echo htmlspecialchars($log['ip']); ?></td>
<td><?php echo $log['method']; ?></td>
<td><?php echo htmlspecialchars($log['url']); ?></td>
<td><?php echo $log['user_id']; ?></td>
<td><?php echo substr($log['session_id'], 0, 8) . '...'; ?></td>
<td><button onclick="showDetail(<?php echo htmlspecialchars(json_encode($log)); ?>)">查看详情</button></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="7">暂无日志记录</td></tr>
<?php endif; ?>
</tbody>
</table>
<!-- 分页 -->
<div class="pagination">
<?php if ($totalPages > 1): ?>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<a href="?page=<?php echo $i; ?>&date=<?php echo $_GET['date'] ?? ''; ?>"><?php echo $i; ?></a>
<?php endfor; ?>
<?php endif; ?>
</div>
<!-- 详情弹窗 -->
<div id="detailModal" class="detail-modal">
<div class="modal-content">
<span onclick="closeDetail()" style="float:right;cursor:pointer;">关闭</span>
<h3>日志详情</h3>
<div id="detailContent"></div>
</div>
</div>
<script>
function showDetail(data) {
document.getElementById('detailModal').style.display = 'block';
var content = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
document.getElementById('detailContent').innerHTML = content;
}
function closeDetail() {
document.getElementById('detailModal').style.display = 'none';
}
</script>
</body>
</html>
使用示例
<?php
// 初始化日志系统
$accessLogger = new AccessLogger('/path/to/logs/');
// 记录访问
$accessLogger->log([
'action' => 'user_login',
'username' => 'admin',
'is_success' => true
]);
// 记录访问日志在入口文件
// config.php 或 index.php
set_error_handler(function($errno, $errstr, $errfile, $errline) {
$logger = ExceptionLogger::getInstance();
$logger->logError($errno, $errstr, $errfile, $errline);
return true;
});
register_shutdown_function(function() {
$error = error_get_last();
if ($error) {
$logger = ExceptionLogger::getInstance();
$logger->logError($error['type'], $error['message'], $error['file'], $error['line']);
}
});
?>
安全最佳实践
<?php
/**
* 安全日志记录类 - 防止日志注入
*/
class SecureLogger {
private function sanitize($data) {
if (is_array($data)) {
return array_map([$this, 'sanitize'], $data);
}
// 移除控制字符
$data = preg_replace('/[\x00-\x1F\x7F]/', '', $data);
// 防止注入
$data = str_replace(
["\r\n", "\r", "\n", "\0"],
['', '', '', ''],
$data
);
return $data;
}
public function log($data) {
$cleanData = $this->sanitize($data);
// 日志写入逻辑...
}
}
// 日志文件权限设置
$logger = new AccessLogger();
if (function_exists('shell_exec')) {
shell_exec('chmod 600 ' . $logger->getLogFile());
}
?>
这个日志系统包含:
- 完整的访问日志记录
- 异常和错误日志
- 日志轮转机制
- 日志查看和查询界面
- 安全防护措施
- 数据过滤和清洗功能