PHP项目怎么实现后台操作日志?

wen PHP项目 35

本文目录导读:

PHP项目怎么实现后台操作日志?

  1. 使用数据库记录日志
  2. 使用中间件/AOP方式(推荐)
  3. 使用事件驱动的方式
  4. 最佳实践建议
  5. 日志查询功能
  6. 注意事项

在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);
    }
}
?>

注意事项

  1. 性能考虑:日志操作不应影响主业务流程,建议使用异步记录
  2. 存储策略:定期清理过期日志,或使用日志归档
  3. 安全防护:日志本身也需要保护,防止被篡改
  4. 合规性:遵守数据保护法规(如GDPR),不记录敏感信息
  5. 可追溯性:确保日志的完整性和不可否认性

推荐在实际项目中选择中间件自动记录 + 关键操作手动记录的组合方案,既保证全覆盖又灵活可控。

抱歉,评论功能暂时关闭!