thinkphp 6 审计日志应优先使用中间件而非模拟aop:统一通过adminlogmiddleware拦截请求,用log::channel('admin')写入结构化日志,显式指定渠道避免误入默认通道;status判断需结合异常捕获与业务code,用户id须通过authservice::id()兼容多守卫与认证方式。

ThinkPHP 6 的 middleware 比 AOP 更直接有效
ThinkPHP 原生不支持 Java 那种注解式 AOP,硬套 @Before 或 @Around 会绕远路、难维护。真正落地快、可控强的方式是用中间件 + 请求上下文拦截,而不是折腾自定义 AOP 扩展。
常见错误是试图在控制器方法里手动调 Log::write(),结果漏掉异常分支、没捕获请求参数、日志格式不统一——本质是把审计逻辑和业务混在一起了。
- 所有后台操作入口都走
app/middleware/AdminLogMiddleware.php,统一拦截think\Request和think\Response - 用
$request->action()+$request->controller()定位操作,比解析路由规则更稳 - 敏感操作(如删除、修改状态)需额外检查
$request->isPost()或$request->isDelete(),避免 GET 请求误记 - 别在中间件里直接写数据库,先塞进
Log::channel('admin')->info(),再由日志驱动异步落库(否则拖慢响应)
Log::channel('admin') 要提前配置好驱动和级别
默认的 file 驱动不适合审计日志:查起来慢、没结构、权限难控。必须单独配一个 admin 渠道,指向数据库或 Elasticsearch。
容易踩的坑是只改了 config/log.php 里的 channels.admin,却忘了在 app/middleware/AdminLogMiddleware.php 中显式调用 Log::channel('admin') —— 否则日志全进默认 single 渠道,查都查不到。
- 配置示例:
'admin' => ['type' => 'think', 'path' => '../runtime/admin_log/', 'level' => ['info', 'error']] - 如果选数据库驱动,
type设为database,并确保log_table字段含user_id、ip、method、url、input、result_code -
input字段建议用json_encode($request->param(), JSON_UNESCAPED_UNICODE),但要过滤密码类字段(如password、token)
记录失败操作时,$response->getCode() 不可靠
ThinkPHP 的 $response->getCode() 在异常未被捕获时返回 200,根本不能反映真实结果。真正能判断“操作是否成功”的,是看有没有抛出 ValidateException、DbException,或者响应体是否含 'code' => 0 这类业务约定。
典型错误是只写 Log::info(..., ['status' => $response->getCode()]),结果所有 500 错误都记成 200,审计完全失效。
- 正确做法:在中间件
after()方法里,用try...catch包住$response = $next($request),捕获异常后手动设status = 'fail' - 若项目已约定返回
['code' => 0]表成功,就用json_decode($response->getContent(), true)['code'] ?? -1判断(注意$response->getContent()可能为空) - 对跳转类操作(如
redirect()),$response->getStatusCode()是 302,但业务上可能已成功,需结合$request->isAjax()和响应头Location综合判断
用户身份不能只靠 session('admin_id')
后台登录态常被存在 session,但 session 可能被清空、过期、跨域丢失,单靠 session('admin_id') 会导致日志里大量 user_id = null,查问题时根本没法追溯。
更麻烦的是,有些接口走 token(如 JWT),有些走 session,混着用会让日志字段不一致。
- 统一从
think\Auth或自定义AuthService::id()获取当前管理员 ID,它内部自动兼容 session/token/cookie 多种方式 - 如果用了多守卫(如
admin和api),务必在中间件开头加Auth::guard('admin')->check(),避免非后台请求混入日志表 - 记录
user_agent和$request->ip()时,要处理代理场景:$request->header('x-real-ip') ?: $request->ip()
最复杂的不是怎么记,是怎么保证每条日志都有可回溯的上下文——比如并发下多个请求共用一个 Log::channel() 实例,可能串日志;或者中间件执行顺序错位,导致 before 拿不到用户信息。这些细节不压平,日志就是摆设。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










