应统一使用 UserActivityLogger 服务类记录用户行为日志,强制包含用户 ID、IP、URL、目标模型 ID 并脱敏敏感字段;避免事务内同步写日志,推荐队列异步落库;表结构须建 user_id + created_at 联合索引,高频场景加 action 索引;采用中间件+事件+显式调用混合方案覆盖全操作类型。

用 Log 类直接写日志太粗糙,用户行为根本对不上人
默认的 Log::info() 只记内容和时间,没用户 ID、没请求上下文、没操作目标(比如修改了哪条 $user->id),查问题时只能靠猜。真实场景里,你得知道「谁在什么时候对什么做了什么」。
实操建议:
- 别在控制器里零散调用
Log::info(),统一收口到一个服务类,比如UserActivityLogger - 构造日志内容时强制包含:
auth()->id()、request()->ip()、request()->fullUrl()、关键模型 ID(如$post->id) - 避免记录敏感字段:不写
$request->password,改用'password' => '***'脱敏
DB::table('activity_logs') 手动插入性能差,且事务里容易丢日志
用原生 SQL 插入日志,看似可控,但一遇到数据库事务回滚,日志就跟着消失——用户删了一条订单,日志没留下,审计直接断档。
实操建议:
- 用 Laravel 的
DB::transaction()+DB::commit()后再写日志,或更稳:用DB::unprepared()绕过事务 - 推荐改用队列驱动:把日志写入任务推到
database或redis队列,用php artisan queue:work --once异步落库,不影响主流程响应 - 表结构至少含:
user_id、action(如'updated_post')、subject_type、subject_id、ip、user_agent
监听 eloquent.updated 事件不能覆盖所有操作类型
只监听 Eloquent 事件,漏掉大量非模型层行为:登录登出、菜单点击、Excel 导出、API Token 刷新……这些压根不走模型,事件监听器完全收不到。
实操建议:
- 核心动作必须手动触发日志:在
AuthController@login里调用UserActivityLogger::log('logged_in') - 用中间件捕获通用行为:写个
LogUserActivity中间件,在handle()里判断是否登录、是否是 POST/PUT/DELETE 请求,自动记录 - 别依赖单一事件源;混合使用:事件 + 中间件 + 显式调用,按场景选最稳的那个
日志量大时查 activity_logs 表卡顿,WHERE user_id = ? 没索引
上线两周后查某用户操作记录要 8 秒,explain 显示全表扫描——因为忘了给 user_id 和 created_at 加联合索引。
实操建议:
- 建表迁移里必须加:
$table->index(['user_id', 'created_at']);高频查动作类型就再加index(['action', 'created_at']) - 单表超 100 万行就分表:按月分表(
activity_logs_202404),用视图或查询路由聚合 - 别在日志表里
JOIN users查用户名;写日志时存user_name快照字段,避免关联拖垮查询
最常被忽略的是「日志写入时机」:同步写磁盘最稳,但慢;异步快,但进程崩溃可能丢最后几条。没有银弹,得看业务容忍度——金融类操作必须同步落库,后台管理类可以接受秒级延迟。











