06 PM2 与 Nginx 日志
1. 先分清三类日志
应用日志
Express/Pino 输出的业务与错误信息
PM2 日志
收集应用 stdout/stderr,并记录 PM2 daemon 自身事件
Nginx 日志
记录公网请求、状态码、代理连接和配置错误
它们观察的是不同层次,不能互相替代。
2. PM2 默认日志在哪里
PM2 官方默认目录:
$HOME/.pm2/logs/
部署用户为 deploy 时通常是:
/home/deploy/.pm2/logs/
应用名为 nloop-api,常见文件类似:
nloop-api-out.log
nloop-api-error.log
实际文件名可能带实例 ID,应以 pm2 show nloop-api 和目录内容为准。
3. stdout 与 stderr
console.log("server started");
console.error("database connection failed");
大致流向:
console.log / Pino info
→ stdout
→ PM2 out log
console.error / Pino error
→ stderr
→ PM2 error log(取决于 logger 输出配置)
如果 Pino 全部以 JSON 输出到 stdout,错误也可能出现在 out log。日志级别属于 JSON 字段,不一定等于操作系统 stderr。
4. 查看 PM2 日志
pm2 logs
pm2 logs nloop-api
pm2 logs nloop-api --lines 200
pm2 logs nloop-api --err
pm2 logs nloop-api --out
pm2 logs nloop-api --lines 200 --nostream
实时追踪日志使用 pm2 logs;只想打印最近内容并退出,使用 --nostream。
PM2 daemon 自身问题还可以查看:
$HOME/.pm2/pm2.log
5. ecosystem 中的最小日志配置
第一阶段保留 PM2 默认目录,只加时间:
module.exports = {
apps: [
{
name: "nloop-api",
script: "./dist/server.js",
cwd: "/var/www/nloop/current",
instances: 1,
exec_mode: "fork",
time: true,
},
],
};
这样不需要先处理 /var/log 权限。
需要自定义文件时:
{
out_file: "/var/log/nloop/app-out.log",
error_file: "/var/log/nloop/app-error.log",
merge_logs: true,
time: true,
}
必须先创建目录并授予部署用户明确权限。不要为了省事执行:
chmod -R 777 /var/log/nloop
6. 结构化应用日志
当前项目已经包含 Pino 和 pino-http,可以让日志以数据形式输出:
{
"level": 30,
"time": 1787000000000,
"requestId": "req-123",
"method": "GET",
"route": "/api/users",
"statusCode": 200,
"responseTime": 18
}
推荐原则:
- 应用输出结构化 JSON 到 stdout;
- PM2 负责托管和落盘;
- 不让每个 Controller 自己打开同一个文件追加;
- 使用 request ID 串联 Nginx、Express 和数据库问题;
- 生产环境不要使用漂亮化多行输出作为唯一日志格式。
7. PM2 日志轮转
PM2 默认日志文件会持续增长。官方提供 pm2-logrotate 模块:
pm2 install pm2-logrotate
安装后应查看实际配置:
pm2 conf pm2-logrotate
pm2 list
也可以使用 Linux 原生 logrotate。新人阶段先选择一种方案,不要让 PM2 模块和系统 logrotate 同时对同一文件执行不一致策略。
上线初期至少要做到:
- 有最大文件大小或轮转周期;
- 有保留数量;
- 旧日志压缩;
- 磁盘空间监控;
- 知道如何查当前日志和历史日志。
8. pm2 flush 的边界
pm2 flush
会清空 PM2 当前管理的日志。它不是日志轮转方案,也不应该成为磁盘满了之后的日常补救。
执行前确认:
- 故障日志是否已经保存;
- 是否需要审计;
- 是否影响事故调查;
- 磁盘满的根因是否已经处理。
9. Nginx 访问日志和错误日志
可以在 server 中配置:
server {
access_log /var/log/nginx/nloop-access.log;
error_log /var/log/nginx/nloop-error.log warn;
# location ...
}
access log
主要回答:
谁请求了什么
状态码是多少
返回了多少字节
使用什么 User-Agent
请求花了多久(需要格式中包含耗时变量)
error log
主要记录:
- 无法连接 Express 上游;
- 上游提前关闭连接;
- 文件权限错误;
- 配置或运行时错误;
- timeout;
- TLS 问题。
10. 增加一个实用访问日志格式
log_format 通常定义在 http 上下文:
log_format api_main
'$remote_addr - $host [$time_local] '
'"$request" $status $body_bytes_sent '
'request_time=$request_time '
'upstream_time=$upstream_response_time '
'upstream_status=$upstream_status '
'"$http_user_agent"';
server 中使用:
access_log /var/log/nginx/nloop-access.log api_main;
两个耗时字段很有帮助:
request_time Nginx 处理整个请求的时间
upstream_response_time 等待上游 Express 的时间
它们可以帮助判断时间主要消耗在代理上游还是其他阶段。
11. Nginx 日志轮转
Linux 发行版通常已经为 /var/log/nginx/*.log 提供 logrotate 配置。应先检查:
sudo cat /etc/logrotate.d/nginx
Nginx 支持重新打开日志文件;发行版的 logrotate 配置通常会在轮转后通知 Nginx。不要只手工重命名正在写入的文件,然后假设 worker 会自动切换到新文件。
12. 502 的日志排查路线
浏览器收到:
502 Bad Gateway
按顺序检查:
sudo tail -n 100 /var/log/nginx/nloop-error.log
pm2 list
pm2 logs nloop-api --lines 100 --nostream
curl -i http://127.0.0.1:8080/health
ss -lntp | grep 8080
常见原因:
- PM2 应用没有启动;
- Express 崩溃重启;
- 监听端口不一致;
- Express 只监听了另一个地址;
.env未加载导致启动失败;- Nginx
proxy_pass地址写错; - 防火墙或 SELinux 策略阻止连接。
13. 日志隐私和安全
不要记录:
- 密码;
- Authorization Token;
- Cookie;
- 数据库连接密码;
- 完整身份证号或支付信息;
- 文件正文;
- 完整敏感请求体。
URL query 也可能进入 Nginx access log,所以不要把 Token 和密码放入 URL。
复盘题
- PM2 out/error log 与日志级别是什么关系?
- 为什么第一版推荐使用
$HOME/.pm2/logs? request_time和upstream_response_time分别表示什么?- 为什么
pm2 flush不能代替日志轮转? - Nginx 返回 502 时应先看哪些日志和状态?