需启用 MongoDB 驱动命令监控(monitorCommands=true),监听 find/insert 等操作并过滤 fs.files/fs.chunks 集合,结合应用层打点与网络日志实现文件级审计。

怎么开启 GridFS 操作的原始查询日志
MongoDB 驱动本身不自动记录 GridFS 的 upload 或 download 行为——它只记录底层的 insertOne、find 等 BSON 操作。所以想看到“谁在读哪个大文件”,得手动打开驱动级查询日志,而不是依赖 Laravel 或 Spring Boot 的抽象层日志。
以 PHP(Laravel + MongoDB PHP Driver)为例,你不能靠 DB::enableQueryLog() 捕获 GridFS 操作,因为它只对 Eloquent 和 DB:: 查询生效,而 GridFS 是通过 GridFSBucket 实例直连的。正确做法是启用 MongoDB PHP Driver 的「命令监控」:
- 在连接选项中添加
'monitorCommands' => true - 注册一个
CommandStartedEvent监听器,过滤find、insert、delete等命令,重点看collectionName是否为fs.files或fs.chunks - 注意:该日志会包含完整 payload(比如
filename、_id),但不带用户上下文(如 HTTP 请求 ID 或用户 ID),需自行关联
为什么不能只靠 mongostat 或 mongotop 监控 GridFS 文件访问
mongostat 只显示每秒操作数、锁时间、内存用量等聚合指标;mongotop 能看到 fs.files 和 fs.chunks 的读写时长,但无法区分是「用户 A 下载 report.pdf」还是「后台任务清理旧备份」——它们都只是对同一集合的 find。
真正要审计文件级行为,必须结合三类信息:
- 应用层日志:在调用
$bucket->openDownloadStream()前打点,记录filename、user_id、ip、start_time - 数据库命令日志:确认该操作确实触发了
fs.chunks的批量find - 网络/代理日志(如 Nginx):补全客户端真实 IP 和请求路径,避免被负载均衡器掩盖
漏掉任意一环,就可能出现「日志里查到有读 chunks,但不知道是谁、为什么读、读了多大」的情况。
自定义文件访问日志模型要注意的 3 个坑
很多人直接建一个 gridfs_access_log 集合,存 filename、user_id、action(upload/download)、size、ip,结果上线后发现写入延迟高、查询慢、字段意义模糊。
关键避坑点:
-
filename字段别存完整路径(如/uploads/2026/03/report_v2.pdf),应标准化为哈希前缀 + 原始名(如sha256_abc123_report_v2.pdf),避免索引膨胀和通配符查询失效 - 别把
size当成文件大小——GridFS 中单次openDownloadStream()可能只读部分 chunk,真正文件大小得从fs.files集合查length字段,日志里应存file_id而非仅filename - 时间戳必须用
UTCDateTime(PHP 驱动)或ISODate(Shell),别用字符串或本地时间,否则跨时区聚合会错乱
上传/下载慢?先确认是不是 chunkSize 拖累的
默认 chunkSizeBytes = 255 * 1024(255 KiB),对小文件(fs.chunks 文档,写入放大明显,且每个 chunk 插入都是一次 round-trip。
实测调优建议:
- 若业务以「大文件上传为主」(如 >50 MB),可将
chunkSizeBytes提高到1024 * 1024(1 MiB)甚至4 * 1024 * 1024(4 MiB),减少文档数量和网络往返 - 但注意:增大 chunkSize 会提高内存占用——
openUploadStream()默认缓冲整个 chunk,服务端需预留足够内存,否则触发 GC 或 OOM - 别全局改;应在创建
GridFSBucket实例时按场景传GridFSBucketOptions,比如上传视频用大 chunk,上传头像用默认值
最常被忽略的一点:chunkSize 修改后,新旧文件混存于同一 bucket,files 集合里的 chunkSize 字段是每个文件独立的,日志分析时如果只查平均 chunk 大小,会误判整体性能瓶颈。










