nginx 问题诊断
1. 定位 nginx 日志位置与规则
sudo bash <<'EOF'
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
nginx -T >"$TMP" 2>/dev/null
echo "=== compile-time default log paths ==="
DEF=$(nginx -V 2>&1 | grep -oE -- '--(error-log-path|http-log-path)=[^ ]+' || true)
if [ -n "$DEF" ]; then printf '%s\n' "$DEF"
else echo "(not in nginx -V)"; DEF="--error-log-path=/var/log/nginx/error.log"
fi
echo ""
echo "=== log rules from nginx -T ==="
awk '
/^[[:space:]]*log_format[[:space:]]/ {p=1}
p {print; if (/;/) p=0; next}
/^[[:space:]]*(error_log|access_log)[[:space:]]/ {print}
' "$TMP"
grep -qE '^[[:space:]]*error_log[[:space:]]' "$TMP" \
|| echo "(no error_log directive; use compile-time default)"
echo ""
echo "=== log files ==="
{
printf '%s\n' "$DEF" | cut -d= -f2-
awk '/^[[:space:]]*(error_log|access_log)[[:space:]]/ {
gsub(/;/,"",$2)
if ($2!="" && $2!="off" && $2!~/^syslog:/) print $2
}' "$TMP"
} | sed '/^$/d' | sort -u | while read -r f; do
ls -lh "$f" 2>/dev/null || echo "MISSING $f"
done
EOF
2. 筛选 access log 最近 2 小时常见报错
默认路径在命令末尾;若为空自动尝试 .1 与 /etc/nginx/logs/access.log。时间窗按日志末行往前约 2 小时(避免轮转后按“当前时刻”筛空)。
sudo bash -s -- /var/log/nginx/access.log <<'EOF'
CAND=$1
LOG=
for f in "$CAND" "${CAND}.1" /etc/nginx/logs/access.log /var/log/nginx/access.log.1; do
[ -s "$f" ] && [ -r "$f" ] && LOG=$f && break
done
[ -n "$LOG" ] || LOG=$CAND
echo "=== log: $LOG (cand=$CAND) ==="
[ -r "$LOG" ] && [ -s "$LOG" ] || { echo "log missing or empty"; exit 1; }
RAW=$(tail -n 1 "$LOG" | grep -oE '[0-9]{2}/[A-Za-z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}' | tail -n 1)
[ -n "$RAW" ] || { echo "no timestamp in last line"; exit 1; }
DAY=${RAW%%/*}; R1=${RAW#*/}; MON=${R1%%/*}; R2=${R1#*/}; YEAR=${R2%%:*}; HMS=${R2#*:}
EPOCH=$(date -d "$DAY $MON $YEAR $HMS" +%s)
H0=$(date -d "@$EPOCH" +%d/%b/%Y:%H)
H1=$(date -d "@$((EPOCH-3600))" +%d/%b/%Y:%H)
H2=$(date -d "@$((EPOCH-7200))" +%d/%b/%Y:%H)
echo "=== window by log end: $H2 $H1 $H0 (last=$RAW) ==="
[ "$(( $(date +%s) - EPOCH ))" -gt 7200 ] && echo "NOTE: log end older than 2h wall clock"
TMP=$(mktemp); DIR=$(mktemp -d)
trap 'rm -f "$TMP"; rm -rf "$DIR"' EXIT
tail -n 300000 "$LOG" | grep -E "\[($H2|$H1|$H0):" >"$TMP"
echo "=== matched lines: $(wc -l <"$TMP") ==="
echo ""
echo "=== status counts ==="
sed -nE 's/.*"[A-Z]+ [^"]*" ([0-9]{3}) .*/\1/p' "$TMP" | sort | uniq -c | sort -rn | head -n 20
for code in 499 404 504 502 500 403 401 408 429; do
( sed -nE "s/.*\"[A-Z]+ ([^ \"?]+)[^\"]*\" $code .*/\1/p" "$TMP" \
| sort | uniq -c | sort -rn | head -n 15 >"$DIR/$code" ) &
done
wait
for code in 499 404 504 502 500 403 401 408 429; do
echo ""
echo "=== HTTP $code top URIs ==="
cat "$DIR/$code"
done
EOF
常见状态码说明
| 状态码 | 说明 |
|---|---|
| 499 | 客户端在服务端响应完成前断开(超时、取消请求、上游过慢) |
| 404 | 资源不存在(路径错误、静态文件缺失、路由未配置) |
| 504 | 网关超时(等上游超时,查 proxy_read_timeout 与后端耗时) |
| 502 | 坏网关(上游不可达、进程退出、端口/协议错误) |
| 500 | 服务端内部错误(应用异常或后端处理失败) |
| 403 | 禁止访问(权限、deny、目录浏览关闭等) |
| 401 | 未认证(登录/Auth/Token 缺失或无效) |
| 408 | 请求超时(客户端发送过慢或空闲超时) |
| 429 | 请求过多(限流或防刷触发) |