Winston 无法直接监控 MongoDB 慢查询,必须依赖服务端 profiling 或驱动层命令钩子提供数据源;需先启用 db.setProfilingLevel(1, { slowms: 500 }),再轮询 system.profile 并配合 commandSucceeded 钩子实现双视角监控。

Winston 本身不感知 MongoDB 查询快慢,它只是日志“搬运工”;想用 Winston 监控慢查询,必须先让慢查询信息产生出来——而这个源头只能是 MongoDB 服务端的 db.setProfilingLevel() 或 Node.js 驱动层的命令钩子,Winston 只负责记录它们输出的内容。
为什么直接用 Winston 记录 MongoDB 慢查询会失败
常见错误是:只在 Node.js 里配好 Winston,再写个 logger.warn('slow query'),就以为能捕获慢查询。但实际根本不会触发——因为:
- MongoDB 默认关闭 profiling,
system.profile集合压根不存在,没东西可查 - Winston 不监听数据库命令生命周期,它不知道
find或aggregate执行了多久 - 即使你手动在查询后判断耗时并调用
logger.warn(),也漏掉了连接超时、认证失败、网络中断等未到达驱动层的异常
必须先在 MongoDB 服务端开启 profiling
这是所有监控的前提,跳过这步,Winston 再怎么配置都收不到原始慢查询数据:
- 连上目标数据库:
use your_db_name - 执行:
db.setProfilingLevel(1, { slowms: 500 })(生产环境建议 ≥500ms,避免日志爆炸) - 验证是否生效:
db.getProfilingStatus()应返回{ "was": 1, "slowms": 500 } - 注意:
profile: 2记录全部操作,严禁上生产;profile: 0关闭,什么都不会记
把 system.profile 轮询结果喂给 Winston
Winston 不会自动读 system.profile,你需要自己写轮询逻辑,并把查到的慢操作交给 Winston 记录:
- 必须手动建索引:
db.system.profile.createIndex({ ts: 1 }),否则每次轮询都是全表扫描 - 轮询时带时间游标:
db.system.profile.find({ ts: { $gt: lastTs } }).sort({ ts: 1 }),lastTs是上一次查到的最大ts(ISODate 类型) - 轮询间隔不能低于 1s,太密会让轮询本身变成慢操作,反向拖垮数据库
- 拿到文档后,用 Winston 记录关键字段:
logger.warn('MongoDB slow op', { op: doc.op, ns: doc.ns, millis: doc.millis, ts: doc.ts })
用 driver 的 commandSucceeded 钩子 + Winston 做客户端视角补充
这是更轻量、更实时的方式,但和上面的服务端 profiling 是两套独立机制,必须配合使用才完整:
- 从
mongoose.connection.client(或原生MongoClient实例)监听:commandStarted和commandSucceeded - 在
commandStarted里打点:event.durationStart = process.hrtime() - 在
commandSucceeded里算耗时并交由 Winston:if (ms > 500) logger.warn('Slow cmd', { cmd: event.commandName, ms }) - 注意:这个方式捕获不到服务端锁等待、磁盘 IO 延迟、网络抖动等环节,只反映“从 Node.js 发出到收到响应”的总耗时
真正容易被忽略的是:Winston 只管写日志,不解决“日志从哪来”这个根本问题。慢查询监控的本质是双视角对齐——服务端 profiling 给你真实执行痕迹,客户端钩子给你请求链路耗时,两者时间戳、操作名、上下文必须能关联比对,否则你看到的只是两套互相矛盾的“慢”。


















