# thinkphp-mq **Repository Path**: zcclxx/thinkphp-mq ## Basic Information - **Project Name**: thinkphp-mq - **Description**: 测试 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-15 - **Last Updated**: 2026-05-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # zcclxx/think-api-request-log ThinkPHP 模块 API 请求日志:中间件采集请求 → RabbitMQ 异步入队 → 消费者按月分表落库;MQ 失败时自动兜底写库。 适用于 ThinkPHP 6 / 8 多应用项目,在 **api 等模块** 的 `middleware.php` 中按需挂载。 ## 功能 - 记录 URL、方法、pathinfo、控制器/操作、IP、User-Agent、请求参数、请求头、HTTP 状态码、响应体 - 敏感参数与请求头自动脱敏(可配置) - 响应体超长自动截断(默认 15000 字符) - RabbitMQ 异步入队,降低接口耗时 - MQ 发布失败时同步写库兜底 - 按月自动建表:`{表前缀}api_request_log_YYYYMM` - 控制台消费:`once`(cron)/ `timer`(Workerman 定时) ## 环境要求 - PHP >= 7.1.0(ThinkPHP 6 实际建议 7.2+,TP8 需 PHP 8.0+) - ThinkPHP ^6.0 或 ^8.0 - php-amqplib/php-amqplib ^3.7 - MySQL(落库) - RabbitMQ(`amqp.enable=true` 时) - Workerman(可选,仅 `timer` 消费模式需要) ## 安装 ### 1. Composer 引入 **Packagist / 私有仓库:** ```bash composer require zcclxx/think-api-request-log ``` **本地路径开发:** 在业务项目 `composer.json` 增加: ```json { "repositories": [ { "type": "path", "url": "../mq", "options": { "symlink": true } } ], "require": { "zcclxx/think-api-request-log": "^1.0" }, "config": { "allow-plugins": { "zcclxx/think-api-request-log": true } } } ``` 然后执行(**不要**只写包名不带版本,path 仓库建议用 `^1.0` 或 `@dev`): ```bash composer update zcclxx/think-api-request-log ``` 若曾安装旧包名 `jg/mq-amqp`,请先移除再装: ```bash composer remove jg/mq-amqp composer require zcclxx/think-api-request-log:^1.0 ``` 安装后 ThinkPHP 会通过 `extra.think.services` 自动加载 `Zcclxx\ApiRequestLog\ServiceProvider`,无需手动注册。 `composer require` / `composer update` 结束时,包内 **Composer 插件** 会交互式询问: ```text 是否将 api-request-log 配置文件发布到 config/ 目录? [Y/n] ``` - 输入 **Y** 或直接回车:生成 `config/amqp.php`、`config/api_request_log.php`(仅复制尚不存在的文件) - 输入 **n**:跳过;包仍会使用内置默认配置,可稍后手动发布 - **CI / 非交互**(`composer install --no-interaction`):自动跳过,不弹窗 未发布时功能照常可用(`ServiceProvider` 会合并包内默认配置);发布只是为了方便在项目中直接改连接信息。 ## 发布稳定版(解决 `minimum-stability (stable)` 无法安装) 业务项目默认 `minimum-stability` 为 **stable**,只有 Packagist 上存在**带稳定标签**的版本时,`composer require zcclxx/think-api-request-log` 才会成功。 ### 方式一:发布到 Packagist(公开稳定版) 1. **准备 Git 仓库**(GitHub / Gitee / GitLab 等),将本包代码推送到默认分支(如 `main` / `master`)。 2. **确认 `composer.json` 中 `name` 为** `zcclxx/think-api-request-log`,与 Packagist 上的 vendor/package 一致。 3. **打 Git 标签**(稳定版必须以 **v 前缀** 的语义化版本为准,不要用仅靠 `composer.json` 里的 `"version"` 代替): ```bash git tag -a v1.0.0 -m "Release 1.0.0" git push origin v1.0.0 ``` 4. 打开 [Packagist](https://packagist.org/) → **Submit** → 填入仓库地址 → 提交。首次会抓取默认分支;**打上 `v1.0.0` 标签后**,Packagist 会出现 **1.0.0** 稳定版。 5. 在业务项目中安装: ```bash composer require zcclxx/think-api-request-log:^1.0 ``` **说明:** 若包已上架 Packagist,建议从 `composer.json` 中**删除** `"version": "1.0.0"` 字段,版本以 **Git 标签** 为准(Packagist 会提示);本地 path 开发可临时保留 `version` 方便解析。 ### 方式二:不发布 Packagist,用私有 Git 源 在业务项目 `composer.json` 中声明 **VCS**,并同样打 `v1.0.0` 标签后推送: ```json { "repositories": [ { "type": "vcs", "url": "https://github.com/你的账号/think-api-request-log.git" } ], "require": { "zcclxx/think-api-request-log": "^1.0" }, "config": { "allow-plugins": { "zcclxx/think-api-request-log": true } } } ``` ### 方式三:仅本地 path(不追求 stable 名) ```json "repositories": [{ "type": "path", "url": "../mq" }], "require": { "zcclxx/think-api-request-log": "^1.0" } ``` 若未打 Git 标签、仅靠 path,可能被识别为 **dev**,此时业务项目需: ```bash composer require zcclxx/think-api-request-log:@dev ``` 或把根项目 `composer.json` 设为 `"minimum-stability": "dev", "prefer-stable": true`(不推荐全局改,优先打 `v1.0.0`)。 ### 2. 配置文件(可选发布) **方式 A:安装时按提示发布**(推荐) 安装包时根据上述提示选择 `Y` 即可。 **方式 B:手动发布** ```bash php think vendor:publish ``` 若 `config/amqp.php` 或 `config/api_request_log.php` 已存在且要覆盖,使用: ```bash php think vendor:publish -f ``` 会在项目 `config/` 下生成: - `config/amqp.php` — RabbitMQ 连接(按环境修改) - `config/api_request_log.php` — 中间件脱敏、用户 ID 等(可选) 也可手动从包内复制:`vendor/zcclxx/think-api-request-log/config/amqp.php` → `config/amqp.php`。 **`config/amqp.php` 默认内容示例(请改为你的 RabbitMQ 信息):** ```php (bool) env('amqp.enable', true), 'host' => env('amqp.host', '127.0.0.1'), 'port' => (int) env('amqp.port', 5672), 'user' => env('amqp.user', 'guest'), 'password' => env('amqp.password', 'guest'), 'vhost' => env('amqp.vhost', '/'), 'heartbeat' => (int) env('amqp.heartbeat', 30), 'connect_timeout' => (float) env('amqp.connect_timeout', 3.0), 'read_write_timeout' => (float) env('amqp.read_write_timeout', 0), 'api_request_log' => [ // 队列名须与 RabbitMQ 管理界面、消费者一致 'queue' => env('amqp.queue.api_request_log', 'api_request_log'), ], ]; ``` ### 3. 环境变量(.env,推荐) ```ini # RabbitMQ amqp.enable = true amqp.host = 127.0.0.1 amqp.port = 5672 amqp.user = guest amqp.password = guest amqp.vhost = / amqp.heartbeat = 30 amqp.connect_timeout = 3.0 amqp.read_write_timeout = 0 amqp.queue.api_request_log = api_request_log ``` 本地无 MQ 时可设 `amqp.enable = false`,中间件会直接写库,便于调试。 ### 4. 中间件配置(可选) 在业务项目 `config/api_request_log.php` 中可覆盖脱敏规则、用户 ID 解析等。包启动时会与内置配置做 `array_replace_recursive` 合并。 **`user_id_resolver` 说明**:日志里的 `user_id` 只来自本闭包,或(未配置有效 callable 时)`tinywan\JWT::getCurrentId()`;**不会**自动使用请求参数里的 `user_id`。须传入 **callable**,不能写字符串 `"user_id"`。从请求头取值时注意:**请求头可被伪造**,适合网关已鉴权并下发可信头的场景;对外 API 更稳妥的是 Session、或解析你们自己的 Token 后再返回用户主键。 示例: ```php function (\think\Request $request) { return (int) ($request->header('X-User-Id', 0)); }, // 方式 B:Session(键名按项目修改;与方式 A 二选一即可) // 'user_id_resolver' => function (\think\Request $request) { // return (int) ($request->session('uid') ?? 0); // }, // 方式 C:业务里挂在 Request 上的属性(需由前置中间件赋值) // 'user_id_resolver' => function (\think\Request $request) { // return (int) ($request->userId ?? 0); // }, 'middleware' => [ 'response_max_chars' => 15000, 'sensitive_params' => ['password', 'token'], 'sensitive_headers' => ['authorization', 'cookie'], ], ]; ``` 队列名、MQ 连接请在 **`config/amqp.php`** 或 `.env` 中配置,不要写在 `api_request_log.php` 里。 ## 在模块中启用中间件 多应用下,在需要记录日志的模块 `middleware.php` 中加入: ```php function (\think\Request $request) { return (string) $request->app(); // 或你自己的逻辑 }, ``` **单路由(示例):** ```php Route::group('api', function () { // ... })->middleware(\Zcclxx\ApiRequestLog\Middleware\ApiRequestLog::class); ``` ## 数据流 ``` HTTP 请求 → ApiRequestLog 中间件(采集、脱敏) → amqp.enable = true → RabbitMQ 队列 → amqp.enable = false → 直接写库 → MQ 发布失败 → 兜底写库 php think api-request-log:consume → 从队列拉取 → 写入 api_request_log_YYYYMM ``` ## 消费队列 安装包后自动注册命令 `api-request-log:consume`。 ### once:拉一批后退出(适合 cron) ```bash # 默认每轮最多 500 条 php think api-request-log:consume once # 自定义条数,0 表示直到队列为空 php think api-request-log:consume once -l 500 ``` cron 示例(每分钟): ```cron * * * * * cd /path/to/project && php think api-request-log:consume once -l 500 >> /dev/null 2>&1 ``` ### timer:Workerman 定时消费 需先安装 `workerman/workerman`。 ```bash # 前台,每 30 秒一批 php think api-request-log:consume timer -i 30 -l 500 # 守护进程 php think api-request-log:consume timer -d -i 30 -l 500 # 停止(Linux) php think api-request-log:consume timer-stop ``` PID 与日志文件位于项目 `runtime/`: - `api_request_log_timer.pid` - `workerman_api_request_log_timer.log` ## 数据表 表名:`{database.connections.mysql.prefix}api_request_log_YYYYMM` 首次写入当月数据时自动建表;旧表会自动补全 `module`、`http_status`、`response_content`、`request_headers` 等字段。 主要字段: | 字段 | 说明 | |------|------| | user_id | 用户 ID,解析失败为 0 | | module | ThinkPHP 多应用名称(如 `api`、`shop`、`admin`) | | url / method / pathinfo | 请求地址信息 | | controller / action | 控制器与操作 | | ip / useragent | 客户端信息 | | params | 请求参数 JSON(敏感字段已脱敏) | | request_headers | 请求头 JSON(敏感头已脱敏) | | http_status | HTTP 状态码 | | response_content | 响应体(过长会截断) | | create_time | 记录时间戳 | ## 配置项说明 ### `config/amqp.php`(`config('amqp.xxx')`) | 键 | 说明 | 默认 | |----|------|------| | `enable` | 是否走 MQ | `true` | | `host` / `port` / `user` / `password` / `vhost` | RabbitMQ 连接 | 见 `config/amqp.php` / `.env` | | `heartbeat` | 心跳秒数 | `30` | | `connect_timeout` | 连接超时(秒) | `3.0` | | `read_write_timeout` | 读写超时(秒),`0` 表示 `max(6, heartbeat*2)` | `0` | | `api_request_log.queue` | 队列名(生产与消费须一致) | `api_request_log` | ### `config/api_request_log.php`(`config('api_request_log.xxx')`) | 键 | 说明 | 默认 | |----|------|------| | `user_id_resolver` | `callable(Request): mixed`(转 int),日志字段 `user_id`;非 callable 则走 JWT 或 0;与请求参数 `user_id` 无自动关联 | `null`(见上文示例与安全说明) | | `module_resolver` | `callable(Request): string`,自定义模块名 | `null`(自动读当前应用名) | | `middleware.response_max_chars` | 响应体最大入库字符数 | `15000` | | `middleware.sensitive_params` | 敏感参数字段名 | 见包内默认配置 | | `middleware.sensitive_headers` | 敏感请求头名 | 见包内默认配置 | ## 目录结构 ``` mq/ ├── composer.json ├── config/ │ ├── amqp.php # RabbitMQ 默认配置(安装后发布到项目) │ └── api_request_log.php # 中间件默认配置 ├── src/ │ ├── ServiceProvider.php # ThinkPHP 服务注册 │ ├── Middleware/ │ │ └── ApiRequestLog.php # 中间件 │ ├── Publisher/ │ │ └── AmqpPublisher.php # MQ 发布 │ ├── Storage/ │ │ └── DatabaseStorage.php # 按月分表写库 │ ├── Amqp/ │ │ └── SimplePublisher.php # AMQP JSON 发布 │ └── Command/ │ └── ApiRequestLogConsume.php └── README.md ``` ## 常见问题 **Q:中间件加了但没有日志?** - 确认请求确实经过该模块中间件 - 检查 `amqp.enable`:为 `true` 时需启动消费者;为 `false` 时直接查当月分表 - 查看 ThinkPHP 日志中 `[api_request]` 相关错误 **Q:队列有消息但库中没有?** - 执行 `php think api-request-log:consume once` 手动消费 - 核对 `config/amqp.php` 中 `api_request_log.queue` 与 RabbitMQ 管理界面中的队列名、vhost 是否一致 **Q:如何获取 JWT 用户 ID?** 安装 `tinywan/jwt` 且未配置 `user_id_resolver` 时会尝试 `getCurrentId()`;否则请在 `config/api_request_log.php` 中配置闭包(见包内 `config/api_request_log.php` 注释与上文示例)。 **Q:安装后必须手动 vendor:publish 吗?** 不必。安装/更新时会交互式询问是否发布;不发布也能用(走包内默认配置 + `.env`)。非交互环境需手动执行 `php think vendor:publish`;若 `config/` 下已有同名文件需覆盖,加 `-f`。 **Q:安装后必须有哪些配置?** - **`config/amqp.php`**:建议发布并填写 RabbitMQ 连接(安装时选 Y,或 `vendor:publish`) - **`config/api_request_log.php`**:可选,用于脱敏与用户 ID **Q:`minimum-stability (stable)` 找不到版本?** 包未在 Packagist 上架、或仓库**没有打** `v1.0.0` 这类稳定标签时会出现。请按上文「发布稳定版」打标签并提交 Packagist,或改用 VCS/path + 明确版本约束。临时可:`composer require zcclxx/think-api-request-log:@dev`(不推荐生产)。 **Q:composer require 报 consistency issue / 找不到包?** 常见原因与处理: 1. **path 仓库包缺少版本**:本包已声明 `"version": "1.0.0"`,请用 `^1.0` 或 `@dev`,不要裸写 `composer require zcclxx/think-api-request-log` 2. **业务项目 `repositories` 未配置 path**:`url` 指向本包目录,如 `"../mq"` 3. **仍引用旧包名 `jg/mq-amqp`**:先 `composer remove jg/mq-amqp` 再装新包 4. **Composer 插件未放行**:在业务项目 `composer.json` 加 `"allow-plugins": { "zcclxx/think-api-request-log": true }` 5. 修改后执行:`composer clear-cache && composer update zcclxx/think-api-request-log -W` **Q:从旧版迁移?** 继续使用 `config/amqp.php` 即可,队列名为 `api_request_log.queue`(或 `.env` 的 `amqp.queue.api_request_log`)。 ## License MIT