本文目录导读:

在PHP项目中实现后台操作日志,通常有几种成熟的方案,以下是几种常见实现方式,从简单到复杂:
使用数据库记录日志
创建日志表
CREATE TABLE `admin_logs` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`admin_id` int(11) NOT NULL COMMENT '操作管理员ID',
`admin_name` varchar(50) NOT NULL COMMENT '操作人',
`type` varchar(20) NOT NULL COMMENT '操作类型:create/update/delete/login',
`module` varchar(50) NOT NULL COMMENT '操作模块',
`action` varchar(100) NOT NULL COMMENT '操作描述',
`request_data` text COMMENT '请求数据',
`ip_address` varchar(45) COMMENT 'IP地址',
`user_agent` varchar(255) COMMENT '浏览器信息',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_admin_id` (`admin_id`),
KEY `idx_type` (`type`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
日志记录类实现
<?php
class AdminLogger {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function log($admin_id, $admin_name, $type, $module, $action, $request_data = null) {
$data = [
'admin_id' => $admin_id,
'admin_name' => $admin_name,
'type' => $type,
'module' => $module,
'action' => $action,
'request_data' => json_encode($request_data),
'ip_address' => $this->getClientIp(),
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
];
$stmt = $this->db->prepare("INSERT INTO admin_logs (admin_id, admin_name, type, module, action, request_data, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
return $stmt->execute(array_values($data));
}
private function getClientIp() {
$ip = '0.0.0.0';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
}
?>
使用中间件/AOP方式(推荐)
定义日志注解(PHP 8+)
<?php
#[Attribute]
class LogAnnotation {
public function __construct(
public string $type,
public string $module,
public string $action
) {}
}
?>
使用中间件自动记录
<?php
class LogMiddleware {
private $logger;
public function __construct(AdminLogger $logger) {
$this->logger = $logger;
}
public function handle($request, $next) {
// 获取当前路由和控制器信息
$route = $request->getRoute();
$controller = $route->getController();
$method = $route->getMethod();
// 执行原操作
$response = $next($request);
// 记录日志
if ($this->shouldLog($controller, $method)) {
$this->logger->log(
$request->admin_id,
$request->admin_name,
$this->getLogType($method),
get_class($controller),
"执行了{$method}操作",
$request->all()
);
}
return $response;
}
private function shouldLog($controller, $method) {
return true; // 根据实际情况决定
}
}
?>
使用事件驱动的方式
<?php
// 定义事件
class AdminOperationEvent {
public $admin_id;
public $admin_name;
public $type;
public $module;
public $action;
public $data;
public function __construct($admin_id, $admin_name, $type, $module, $action, $data = []) {
$this->admin_id = $admin_id;
$this->admin_name = $admin_name;
$this->type = $type;
$this->module = $module;
$this->action = $action;
$this->data = $data;
}
}
// 事件监听器
class LogEventListener {
public function handle(AdminOperationEvent $event) {
// 记录到数据库
$logModel = new AdminLogModel();
$logModel->create([
'admin_id' => $event->admin_id,
'admin_name' => $event->admin_name,
'type' => $event->type,
'module' => $event->module,
'action' => $event->action,
'request_data' => json_encode($event->data)
]);
// 可选:同时记录到文件
$logFile = storage_path('logs/admin_operation.log');
$line = sprintf("[%s] %s %s %s %s\n",
date('Y-m-d H:i:s'),
$event->admin_name,
$event->type,
$event->module,
$event->action
);
file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
}
}
?>
最佳实践建议
日志分级
<?php
class LogLevel {
const INFO = 'info';
const WARNING = 'warning';
const ERROR = 'error';
const CRITICAL = 'critical';
}
// 使用示例
$logger->log(LogLevel::INFO, '用户登录成功');
$logger->log(LogLevel::WARNING, '尝试使用弱密码');
$logger->log(LogLevel::ERROR, '数据更新失败');
?>
敏感信息过滤
<?php
class LogCleaner {
private $sensitiveFields = ['password', 'token', 'secret', 'credit_card'];
public function cleanData($data) {
foreach ($this->sensitiveFields as $field) {
if (isset($data[$field])) {
$data[$field] = '***FILTERED***';
}
}
return $data;
}
}
?>
使用现成日志库
- monolog/monolog:PHP最流行的日志库
- KLogger:轻量级日志类
composer require monolog/monolog
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('admin_operation');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
$log->warning('Admin operation', [
'admin_id' => 123,
'action' => 'delete_user',
'target_id' => 456
]);
?>
日志查询功能
<?php
// 后台日志查询
class AdminLogService {
public function search(array $filters, $page = 1, $perPage = 20) {
$query = AdminLogModel::query();
if (isset($filters['admin_id'])) {
$query->where('admin_id', $filters['admin_id']);
}
if (isset($filters['type'])) {
$query->where('type', $filters['type']);
}
if (isset($filters['start_date'])) {
$query->where('created_at', '>=', $filters['start_date']);
}
if (isset($filters['end_date'])) {
$query->where('created_at', '<=', $filters['end_date']);
}
if (isset($filters['keyword'])) {
$query->where('action', 'like', "%{$filters['keyword']}%");
}
return $query->orderBy('created_at', 'desc')
->paginate($perPage, ['*'], 'page', $page);
}
}
?>
注意事项
- 性能考虑:日志操作不应影响主业务流程,建议使用异步记录
- 存储策略:定期清理过期日志,或使用日志归档
- 安全防护:日志本身也需要保护,防止被篡改
- 合规性:遵守数据保护法规(如GDPR),不记录敏感信息
- 可追溯性:确保日志的完整性和不可否认性
推荐在实际项目中选择中间件自动记录 + 关键操作手动记录的组合方案,既保证全覆盖又灵活可控。