yii2中用logbehavior自动记录模型操作日志需配置行为及log组件targets,注意分类为yii\log\logbehavior;字段级变更需自定义difflogbehavior并快照oldattributes;手动日志用yii::info()并传上下文;审计表须建user_id+log_time等联合索引。

Yii2 中用 LogBehavior 自动记录模型操作日志
直接用 Yii2 内置的 LogBehavior 是最轻量、侵入性最小的方式,它能在 insert、update、delete 时自动写入日志,无需改业务逻辑。
常见错误是只配置了行为但没启用日志目标(FileTarget 或 DbTarget),结果“写了但看不到”。另外,LogBehavior 默认不记录原始数据变更(比如旧值/新值),只记动作和时间。
- 在模型类中
behaviors()方法里添加:
public function behaviors()
{
return [
'log' => [
'class' => \yii\log\LogBehavior::class,
'attributes' => ['created_at', 'updated_at'], // 可选:额外记录字段
],
];
}
- 确保
config/web.php的log组件已配置targets,例如写入数据库:
'targets' => [
[
'class' => 'yii\log\DbTarget',
'levels' => ['info'],
'categories' => ['application'],
'logTable' => '{{%log}}', // 表名需存在且含 message、level、category、log_time 字段
],
]
- 注意:
LogBehavior记录的日志分类是yii\log\LogBehavior,不是application,所以categories要配对,否则日志被过滤掉
自定义行为实现字段级变更审计(DiffLogBehavior)
如果需要记录「哪个字段从 A 变成了 B」,就得自己写行为。Yii 不提供开箱即用的 diff 日志,硬套 LogBehavior 会漏掉关键上下文。
典型踩坑点:在 afterSave 里直接读 $this->oldAttributes,但如果是批量更新(updateAll)或未加载旧值(refresh() 没调),oldAttributes 就是空的;还有时间戳字段(如 updated_at)被自动修改,干扰 diff 判断。
- 建议在
beforeSave保存一份快照($this->_oldSnapshot = $this->getOldAttributes();),再在afterSave做对比 - 排除非业务字段:
array_diff_key($new, $old, array_flip(['id', 'created_at', 'updated_at'])) - 日志内容建议存为 JSON:
json_encode(['user_id' => \Yii::$app->user->id, 'changes' => $diff]),方便后续查 - 别在行为里做 DB 写入(如
LogRecord::save()),应交由Logger或Yii::info(),让日志组件统一处理异步/队列
用 yii\log\Logger 手动打点记录关键操作
不是所有操作都绑定在模型上——比如用户导出 Excel、切换租户、执行定时任务,这些得靠手动日志。这时候别手写 file_put_contents,要用 Yii 原生 Logger 接口,否则日志格式不一致、等级不可控、目标不可配。
容易忽略的是上下文传递:Yii::info('Export started', 'audit', ['user_id' => 123, 'file_type' => 'xlsx']) 第三个参数才是结构化字段,不传就只能在 message 里拼字符串,后期没法查。
- 推荐封装一个
AuditLogger::record()静态方法,自动注入user_id、ip、route - 避免在循环里高频打日志(如逐条处理订单),可先聚合再记一次,否则压垮日志表或文件
- 敏感字段(如手机号、金额)必须脱敏后再进日志,
Logger不做自动过滤
数据库表设计与查询性能注意事项
审计日志表不是越全越好。字段太多、索引没建对,查“张三昨天改过哪些订单”就会变慢,尤其当 log 表单日增长超 10 万行时。
实际部署中最常被跳过的一步:没给 user_id、category、log_time 加联合索引。只加了 log_time 单字段索引,查某个用户的近期操作照样全表扫。
- 基础字段建议至少包含:
id、user_id、category(如order/update)、object_type(Order)、object_id、changes(TEXT 或 JSON)、log_time - 必须建的索引:
INDEX idx_user_time (user_id, log_time)、INDEX idx_object (object_type, object_id) - 不要把大字段(如完整请求体、响应体)塞进日志表,另建扩展表或存对象存储,主表只留摘要
字段变更审计的难点不在代码怎么写,而在什么时候该记、记多少、谁有权看——这些往往要配合 RBAC 和日志归档策略,而不是光靠一个行为搞定。











