用Observer监听模型事件最稳妥,它不依赖第三方包、不污染模型逻辑,通过监听created/updated/deleted/restored事件覆盖全链路操作,配合getChanges()精准记录真实变动。

用 Observer 监听模型事件最稳妥
Laravel 原生没提供开箱即用的“操作日志”组件,但 Observer 是最贴合业务、可控性最强的方式。它不依赖第三方包,也不污染模型逻辑,适合记录谁、什么时候、对哪条数据做了什么。
常见错误是直接在 save() 或 updating() 里写日志逻辑——这会导致事务未提交就落库,日志和实际变更不一致;或者把日志塞进控制器,导致重复代码、漏记录软删除等边缘场景。
- 监听
created、updated、deleted、restored四个事件就够了,覆盖增删改恢复全链路 - 在
handle方法里用$model->getChanges()拿到真实变动字段,避免记录未修改的脏数据 - 别在 observer 里调用
save()或触发其他模型操作,容易引发递归监听或事务异常 - 日志模型建议用
timestamps = false,自己控制created_at,避免和主事务时间戳错位
eloquent-event-recorder 包只适合快速验证
这个包能自动记录所有 Eloquent 操作,看起来省事,但实际项目里容易踩坑:它靠全局监听 eloquent.* 事件,无法区分是否来自 API、命令行或队列;更麻烦的是,它默认记录全部属性(包括密码哈希、token),不加过滤会泄露敏感字段。
- 若真要用,必须重写
shouldRecord()方法,按模型名、字段白名单做判断 - 它不兼容软删除的
forceDeleted事件,删库级数据时会漏记 - 性能上,每次 save 都序列化整个模型,大数据量时明显拖慢响应,不适合高频更新场景
- 配置项
excluded_models和excluded_attributes必须显式声明,空数组 ≠ 不排除
手动在 Controller 里记日志?只适用于极简场景
当你的系统只有两三个关键接口、且权限/上下文明确(比如后台管理员操作订单),直接在 Controller 方法末尾写日志是最透明的做法。但它没法覆盖命令行、API 调用、队列任务里的变更,维护成本随接口增多指数上升。
- 务必从
request()->user()或Auth::id()取操作人,别用 session 或 token 解析,避免上下文丢失 - 记录前先做
$model->isDirty()判断,否则 create 和 update 都走同一段日志逻辑,容易误标“修改”动作 - 不要拼接 SQL 或 raw query 记录,Laravel 的
toSql()不带绑定参数,日志里全是问号,失去可读性 - 如果用了多数据库连接,记得在日志模型里指定
$connection = 'log_mysql',别让它跟着主模型跑错库
注意 updated_at 和日志时间戳的偏差
很多人发现日志里的时间比模型的 updated_at 晚几十毫秒,这不是 bug,是 Laravel 默认开启 timestamps 且在模型保存后才触发 observer —— 中间夹着数据库 round-trip 和事件分发开销。线上排查时若按毫秒级对齐,会误判顺序。
- 日志表的
logged_at字段建议用DB::raw('NOW(3)')插入,和 MySQL 的updated_at同源,减少时钟漂移 - 别依赖日志时间做“操作先后”判断,真正需要严格时序的场景,得用数据库的
version或row_version字段 - 如果你开了
DB::enableQueryLog()做调试,它记录的是查询发起时间,和 observer 实际执行时间不是一回事
真正难的不是记下操作,而是定义清楚“一次操作”的边界:是单个 HTTP 请求?还是一个业务流程(比如下单含创建订单+扣库存+发消息)?后者需要自定义事件 + Saga 模式,observer 就不够用了。











