本文目录导读:

- 方案一:使用成熟的日志库(推荐,适用于大部分项目)
- 方案二:基于数据库的活动日志(适用于需要查询、展示审计记录的CMS/ERP)
- 方案三:使用AOP(面向切面编程)自动记录(高级,适合框架)
- 最佳实践建议
- 简单演示:最小化文件日志实现(无第三方依赖)
在PHP项目中实现活动日志记录,核心目标是追踪用户行为和系统事件,以便于审计、调试和监控。
根据项目规模和复杂度,有以下几种常见且实用的实现方案:
使用成熟的日志库(推荐,适用于大部分项目)
这是最专业、最灵活的方案,常用的库有 Monolog(PHP社区事实上的标准)。
安装 Monolog
composer require monolog/monolog
创建日志服务类(封装)
<?php
// src/Service/ActivityLogger.php
namespace App\Service;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;
class ActivityLogger
{
private Logger $logger;
public function __construct(string $logDir = __DIR__ . '/../../var/log/activity')
{
$this->logger = new Logger('activity');
// 1. 按天轮转的日志文件(推荐,避免单个文件过大)
$handler = new RotatingFileHandler($logDir . '/activity.log', 30, Logger::INFO);
// 2. 自定义日志格式:时间戳 + 通道 + 级别 + 消息 + 上下文
$formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context%\n");
$handler->setFormatter($formatter);
$this->logger->pushHandler($handler);
}
/**
* 记录用户活动
* @param string $action 操作类型(如:user_login, order_create, article_edit)
* @param array $context 上下文信息(如:['user_id'=>123, 'target_id'=>456, 'details'=>'...'])
*/
public function log(string $action, array $context = []): void
{
// 自动附加通用信息(如IP、当前用户ID)
$context = array_merge([
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'CLI',
], $context);
// 使用INFO级别记录活动日志
$this->logger->info($action, $context);
}
}
在代码中使用
// 在控制器或服务中
$activityLogger = new ActivityLogger();
// 记录用户登录
$activityLogger->log('user_login', [
'user_id' => $user->getId(),
'email' => $user->getEmail(),
]);
// 记录创建订单
$activityLogger->log('order_create', [
'user_id' => $user->getId(),
'order_id' => $order->getId(),
'amount' => $order->getTotal(),
]);
输出日志文件示例
[2024-01-15 14:30:00] activity.INFO: user_login {"user_id":1,"email":"a@b.com","ip":"192.168.1.1","user_agent":"Mozilla/5.0"} []
[2024-01-15 14:35:12] activity.INFO: order_create {"user_id":1,"order_id":1001,"amount":99.99,"ip":"192.168.1.1"} []
基于数据库的活动日志(适用于需要查询、展示审计记录的CMS/ERP)
将日志存入数据库,便于前端后台查询和统计。
创建数据表
CREATE TABLE `activity_logs` ( `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `user_id` INT UNSIGNED NULL COMMENT '操作用户ID', `action` VARCHAR(100) NOT NULL COMMENT '操作标识', `target_type` VARCHAR(50) NULL COMMENT '操作对象类型(如:order, user, article)', `target_id` INT UNSIGNED NULL COMMENT '操作对象ID', `description` TEXT NULL COMMENT '人类可读的描述', `context` JSON NULL COMMENT '额外上下文信息', `ip_address` VARCHAR(45) NULL, `user_agent` VARCHAR(255) NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX `idx_user_id` (`user_id`), INDEX `idx_action` (`action`), INDEX `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
创建日志模型/Repository
<?php
// src/Repository/ActivityLogRepository.php
use PDO;
class ActivityLogRepository
{
private PDO $pdo;
public function insert(array $data): void
{
$sql = "INSERT INTO activity_logs (user_id, action, target_type, target_id, description, context, ip_address, user_agent)
VALUES (:user_id, :action, :target_type, :target_id, :description, :context, :ip_address, :user_agent)";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
':user_id' => $data['user_id'] ?? null,
':action' => $data['action'],
':target_type' => $data['target_type'] ?? null,
':target_id' => $data['target_id'] ?? null,
':description' => $data['description'] ?? '',
':context' => json_encode($data['context'] ?? []),
':ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
':user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
]);
}
}
在业务逻辑中调用
// 在删除文章的服务中
$logRepo = new ActivityLogRepository($pdo);
$logRepo->insert([
'user_id' => getCurrentUserId(),
'action' => 'article_delete',
'target_type' => 'article',
'target_id' => $articleId,
'description' => "用户删除了文章《{$articleTitle》}",
'context' => ['title' => $articleTitle, 'status' => 'deleted']
]);
使用AOP(面向切面编程)自动记录(高级,适合框架)
如果项目使用 Symfony 或 Laravel,可以利用事件系统或中间件自动记录。
使用 Laravel 事件监听(推荐)
Laravel 本身有 Activitylog 包可用,但也可以手动实现:
// app/Listeners/LogUserActivity.php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Events\Dispatcher;
class LogUserActivity
{
public function handle(Login $event): void
{
activity()
->performedOn($event->user)
->causedBy($event->user)
->log('用户登录');
}
public function subscribe(Dispatcher $events): array
{
return [
Login::class => 'handle',
// 注册其他事件...
];
}
}
使用 Symfony EventSubscriber
// src/EventSubscriber/ActivityLogSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
class ActivityLogSubscriber implements EventSubscriberInterface
{
private ActivityLogger $logger;
public function __construct(ActivityLogger $logger)
{
$this->logger = $logger;
}
public static function getSubscribedEvents(): array
{
return [
LoginSuccessEvent::class => 'onLoginSuccess',
];
}
public function onLoginSuccess(LoginSuccessEvent $event): void
{
$user = $event->getUser();
$this->logger->log('user_login', ['user_id' => $user->getId()]);
}
}
最佳实践建议
- 不要记录密码、信用卡等敏感信息,可以在记录前使用
unset()或ArrayUtils::omit()过滤。 - 使用统一日志格式:
[时间] [级别] [操作] [上下文(JSON)],便于后续用grep或日志分析工具(如ELK)处理。 - 异步写入(高并发场景):将日志写入消息队列(如Redis列表、RabbitMQ),由独立消费者批量写入数据库或文件,避免拖慢主请求。
- 日志轮转:使用
RotatingFileHandler(Monolog)或 Linux 的logrotate避免日志无限增长撑爆磁盘。 - 定义规范操作类型:使用
domain_action命名,如user.login、order.create、article.delete,便于统一管理。 - 考虑成本:
- 小型项目:Monolog + 文件日志即可。
- 中型项目:数据库日志(便于后台查询)。
- 大型项目:独立日志服务(如 ELK)、消息队列异步写入。
简单演示:最小化文件日志实现(无第三方依赖)
如果你不想引入任何库,可以快速写一个函数:
function logActivity(string $action, array $data = []) {
$logFile = __DIR__ . '/activity.log';
$entry = [
'time' => date('Y-m-d H:i:s'),
'action' => $action,
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
'data' => $data,
];
$line = json_encode($entry) . PHP_EOL;
file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX);
}
// 使用
logActivity('user_register', ['email' => 'test@test.com']);
这个版本最简单,但缺乏轮转、不同级别区分、自定义格式等能力,建议尽快升级到 Monolog。
选择哪种方案取决于你的项目规模、查询需求和开发效率,通常从 Monolog 文件日志 开始,后期需要查询时再增加 数据库日志 支持。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播