Nginx日志文件的位置與基礎(chǔ)查看指南
你是否曾對(duì)著空白的終端發(fā)呆,不知道從哪里下手排查問(wèn)題?別慌——你不是一個(gè)人,本文將從零開(kāi)始,徹底搞懂Nginx日志的位置、結(jié)構(gòu)、查看方式、分析技巧
引言
你是否曾在服務(wù)器上遇到“網(wǎng)站打不開(kāi)”、“502 Bad Gateway”、“404 Not Found”這類令人抓狂的錯(cuò)誤?
你是否曾對(duì)著空白的終端發(fā)呆,不知道從哪里下手排查問(wèn)題?
你是否在同事說(shuō)“看下Nginx日志”時(shí),心里默默問(wèn):“日志?在哪?怎么打開(kāi)?看啥?”
別慌——你不是一個(gè)人。
每一個(gè)Nginx老司機(jī),都曾是今天的小白。
而今天,你將從零開(kāi)始,徹底搞懂Nginx日志的位置、結(jié)構(gòu)、查看方式、分析技巧,甚至還能用Java寫(xiě)個(gè)小工具自動(dòng)分析日志!
無(wú)論你是剛?cè)胄械倪\(yùn)維新人、前端開(kāi)發(fā)者、后端Java工程師,還是想自己搭個(gè)博客的愛(ài)好者,這篇文章都會(huì)成為你日志排查路上的“導(dǎo)航儀”。
我們不講高深理論,只講你能馬上用上的實(shí)戰(zhàn)技巧。
準(zhǔn)備好你的終端、你的IDE、你的咖啡,我們開(kāi)始吧!
什么是Nginx?為什么日志這么重要?
Nginx(發(fā)音為“engine-x”)是一個(gè)高性能的HTTP和反向代理服務(wù)器,也是當(dāng)今互聯(lián)網(wǎng)最流行的Web服務(wù)器之一。
它被廣泛用于:
- 托管靜態(tài)資源(HTML、CSS、JS、圖片)
- 作為反向代理,將請(qǐng)求轉(zhuǎn)發(fā)給后端應(yīng)用(如Java Spring Boot、Node.js)
- 負(fù)載均衡
- SSL/TLS終止
- 緩存加速
而日志,就是Nginx的“黑匣子”——它默默記錄了每一個(gè)請(qǐng)求的來(lái)龍去脈。
想象一下:
你的網(wǎng)站突然訪問(wèn)量暴增,用戶投訴“頁(yè)面加載慢”。
你登錄服務(wù)器,打開(kāi)Nginx日志,發(fā)現(xiàn)某幾個(gè)IP在高頻請(qǐng)求一個(gè)不存在的API接口,疑似爬蟲(chóng)或攻擊。
你立刻用防火墻封禁這些IP,問(wèn)題解決。
——這就是日志的力量。
日志 = 問(wèn)題的線索,安全的哨兵,性能的鏡子
沒(méi)有日志,你就像在黑暗中開(kāi)車——沒(méi)有導(dǎo)航,沒(méi)有路燈,只有撞墻的可能。
Nginx日志文件默認(rèn)位置在哪?
Nginx的日志文件位置,不是固定的,它取決于你的系統(tǒng)、安裝方式和配置文件。
但!有絕大多數(shù)情況下的標(biāo)準(zhǔn)路徑,我們先記住這些:
Linux系統(tǒng)(Ubuntu / CentOS / Debian)
| 日志類型 | 默認(rèn)路徑 |
|---|---|
| 訪問(wèn)日志(access log) | /var/log/nginx/access.log |
| 錯(cuò)誤日志(error log) | /var/log/nginx/error.log |
?? 為什么是這兩個(gè)?因?yàn)镹ginx在安裝時(shí),會(huì)默認(rèn)使用
/var/log/nginx/作為日志目錄,這是Linux FHS(文件系統(tǒng)層次結(jié)構(gòu)標(biāo)準(zhǔn))推薦的路徑。
macOS(通過(guò)Homebrew安裝)
/usr/local/var/log/nginx/access.log /usr/local/var/log/nginx/error.log
Docker容器中的Nginx
如果你是用Docker部署的Nginx,日志默認(rèn)會(huì)輸出到標(biāo)準(zhǔn)輸出(stdout)和標(biāo)準(zhǔn)錯(cuò)誤(stderr),也就是說(shuō):
docker logs your-nginx-container
就可以看到日志內(nèi)容。
如果你想持久化日志,需要掛載宿主機(jī)目錄:
docker run -d \ -v /host/logs/nginx:/var/log/nginx \ --name my-nginx \ nginx
這樣日志就會(huì)保存在你宿主機(jī)的 /host/logs/nginx/ 目錄下。
自定義配置下的日志位置
Nginx的配置文件通常是:
/etc/nginx/nginx.conf
打開(kāi)它,你會(huì)看到類似這樣的配置:
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
}log_format:定義日志格式access_log:指定訪問(wèn)日志文件路徑和格式error_log:指定錯(cuò)誤日志文件路徑和日志級(jí)別
所以,最準(zhǔn)確的方法是:直接查看你的nginx.conf文件!
你可以用以下命令快速定位:
# 查看Nginx主配置文件路徑 nginx -t # 輸出類似: # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful # 然后查看配置 cat /etc/nginx/nginx.conf | grep -E "access_log|error_log"
小貼士:如果你不確定Nginx配置文件在哪,可以用 which nginx 找到Nginx可執(zhí)行文件,然后運(yùn)行 nginx -V 查看編譯參數(shù),里面會(huì)包含 --prefix=,這就是Nginx的根目錄。
Nginx日志文件詳解:訪問(wèn)日志 vs 錯(cuò)誤日志
Nginx有兩個(gè)核心日志文件,它們的用途完全不同。
訪問(wèn)日志(access.log):誰(shuí)在訪問(wèn)?訪問(wèn)了什么?
這是你每天看得最多的日志。
它記錄了每一個(gè)HTTP請(qǐng)求的詳細(xì)信息。
默認(rèn)格式示例:
192.168.1.100 - - [25/Apr/2024:10:30:22 +0800] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" "-"
我們來(lái)逐項(xiàng)拆解:
| 字段 | 含義 | 示例 |
|---|---|---|
$remote_addr | 客戶端IP地址 | 192.168.1.100 |
$remote_user | 認(rèn)證用戶(通常為-) | - |
$time_local | 請(qǐng)求時(shí)間(本地時(shí)區(qū)) | [25/Apr/2024:10:30:22 +0800] |
$request | 請(qǐng)求方法 + URI + 協(xié)議 | "GET /index.html HTTP/1.1" |
$status | HTTP響應(yīng)狀態(tài)碼 | 200 |
$body_bytes_sent | 發(fā)送給客戶端的字節(jié)數(shù)(不含響應(yīng)頭) | 1234 |
$http_referer | 來(lái)源頁(yè)面(Referer) | "-"(無(wú)來(lái)源) |
$http_user_agent | 客戶端瀏覽器/設(shè)備信息 | "Mozilla/5.0 ... Chrome/123.0..." |
$http_x_forwarded_for | 代理鏈中的原始IP(如果有) | "-" |
你知道嗎?如果你的網(wǎng)站前面有CDN(如Cloudflare)或負(fù)載均衡器,$remote_addr 可能是CDN的IP,而不是真實(shí)用戶IP。這時(shí)就要依賴 $http_x_forwarded_for。
自定義日志格式(進(jìn)階)
你可以在 nginx.conf 中自定義日志格式,比如增加響應(yīng)時(shí)間:
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log detailed;
現(xiàn)在日志會(huì)多出兩個(gè)字段:
$request_time:Nginx處理該請(qǐng)求的總耗時(shí)(秒,精確到毫秒)$upstream_response_time:后端應(yīng)用(如Java服務(wù))響應(yīng)耗時(shí)
這兩個(gè)字段對(duì)性能分析至關(guān)重要!后面我們會(huì)用Java程序分析它們。
訪問(wèn)日志能告訴你什么?
| 問(wèn)題 | 日志線索 |
|---|---|
| 用戶訪問(wèn)慢 | $request_time > 2s |
| 404 頁(yè)面多 | $status = 404,且請(qǐng)求路徑是無(wú)效API |
| 惡意掃描 | 大量請(qǐng)求 /wp-admin, /phpmyadmin, /robots.txt |
| 高頻爬蟲(chóng) | 同一IP在1秒內(nèi)請(qǐng)求50次 |
| 網(wǎng)站被攻擊 | 大量 POST /login 請(qǐng)求,狀態(tài)碼為401/403 |
錯(cuò)誤日志(error.log):Nginx自己在抱怨什么?
訪問(wèn)日志記錄的是“用戶干了啥”,而錯(cuò)誤日志記錄的是“Nginx自己遇到了啥問(wèn)題”。
默認(rèn)錯(cuò)誤日志示例:
2024/04/25 10:31:15 [error] 1234#1234: *5678 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.100, server: example.com, request: "GET /api/user HTTP/1.1", upstream: "http://127.0.0.1:8080/api/user", host: "example.com"
我們來(lái)解析:
| 字段 | 含義 |
|---|---|
2024/04/25 10:31:15 | 時(shí)間戳 |
[error] | 日志級(jí)別(還有 warn, info, notice) |
1234#1234 | 進(jìn)程ID和線程ID |
*5678 | 連接ID |
connect() failed (111: Connection refused) | 錯(cuò)誤原因 |
while connecting to upstream | 正在連接后端服務(wù) |
client: 192.168.1.100 | 請(qǐng)求客戶端 |
server: example.com | 請(qǐng)求的域名 |
request: "GET /api/user HTTP/1.1" | 請(qǐng)求內(nèi)容 |
upstream: "http://127.0.0.1:8080/api/user" | 后端地址 |
常見(jiàn)錯(cuò)誤類型
| 錯(cuò)誤類型 | 原因 | 解決方案 |
|---|---|---|
Connection refused | 后端服務(wù)(如Java應(yīng)用)沒(méi)啟動(dòng)或端口不對(duì) | systemctl status java-app,檢查端口監(jiān)聽(tīng) |
No such file or directory | 請(qǐng)求的靜態(tài)文件不存在 | 檢查 root 路徑是否正確,文件權(quán)限 |
upstream timed out | 后端響應(yīng)太慢(> proxy_read_timeout) | 調(diào)大 proxy_read_timeout,優(yōu)化Java服務(wù)性能 |
Permission denied | Nginx無(wú)權(quán)讀取文件或連接socket | chown -R www-data:www-data /var/www/html |
SSL_do_handshake() failed | SSL證書(shū)配置錯(cuò)誤 | 檢查 .crt 和 .key 文件路徑、權(quán)限、是否過(guò)期 |
注意:error.log 默認(rèn)級(jí)別是 error,意味著只記錄錯(cuò)誤。
如果你想看到更詳細(xì)的調(diào)試信息,可以臨時(shí)改成 debug:
error_log /var/log/nginx/error.log debug;
但!不要在生產(chǎn)環(huán)境長(zhǎng)期開(kāi)啟debug,它會(huì)產(chǎn)生海量日志,拖垮磁盤(pán)和性能!
如何查看Nginx日志?終端命令實(shí)戰(zhàn)
光知道日志在哪還不夠,你得會(huì)“看”。
基礎(chǔ)命令:cat,less,tail
1.cat:一次性全部打?。ㄟm合小文件)
cat /var/log/nginx/access.log
不推薦用于大文件!會(huì)刷屏,卡死終端。
2.less:分頁(yè)查看(推薦!)
less /var/log/nginx/access.log
- 按
Space下一頁(yè) - 按
b上一頁(yè) - 按
/搜索關(guān)鍵詞(如404) - 按
q退出
3.tail:實(shí)時(shí)追蹤最新日志(最常用?。?/h4>
tail -f /var/log/nginx/access.log
-f = follow,實(shí)時(shí)刷新。
你打開(kāi)這個(gè)命令,然后在瀏覽器刷新頁(yè)面,終端立刻會(huì)看到新日志!
tail -f /var/log/nginx/access.log
-f = follow,實(shí)時(shí)刷新。
你打開(kāi)這個(gè)命令,然后在瀏覽器刷新頁(yè)面,終端立刻會(huì)看到新日志!
4.tail -n 50:只看最后50行
tail -n 50 /var/log/nginx/error.log
5.grep:過(guò)濾關(guān)鍵詞(神器?。?/h4>
# 查看所有404錯(cuò)誤
grep " 404 " /var/log/nginx/access.log
# 查看所有502錯(cuò)誤
grep " 502 " /var/log/nginx/error.log
# 查看某個(gè)IP的所有請(qǐng)求
grep "192.168.1.100" /var/log/nginx/access.log
# 查看請(qǐng)求時(shí)間超過(guò)2秒的請(qǐng)求(需要配合awk)
awk '$10 > 2 {print}' /var/log/nginx/access.log
# 查看所有404錯(cuò)誤
grep " 404 " /var/log/nginx/access.log
# 查看所有502錯(cuò)誤
grep " 502 " /var/log/nginx/error.log
# 查看某個(gè)IP的所有請(qǐng)求
grep "192.168.1.100" /var/log/nginx/access.log
# 查看請(qǐng)求時(shí)間超過(guò)2秒的請(qǐng)求(需要配合awk)
awk '$10 > 2 {print}' /var/log/nginx/access.log
為什么是 grep " 404 "?因?yàn)?$status 前后都有空格,避免匹配到 1404、4041 等錯(cuò)誤數(shù)字。
6.awk:按字段提?。〝?shù)據(jù)分析利器)
# 提取所有狀態(tài)碼和訪問(wèn)次數(shù)
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr
# 輸出示例:
# 1234 200
# 234 404
# 89 500
# 提取前10個(gè)最頻繁訪問(wèn)的URL
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
# 提取訪問(wèn)時(shí)間最長(zhǎng)的前5個(gè)請(qǐng)求(假設(shè)你用了detailed格式,第10列是$request_time)
awk '{print $10, $7}' /var/log/nginx/access.log | sort -nr | head -5
7.journalctl(systemd系統(tǒng)專用)
如果你的Nginx是通過(guò)systemd管理的(大多數(shù)現(xiàn)代Linux都是):
# 查看Nginx服務(wù)日志(包含access和error) journalctl -u nginx # 實(shí)時(shí)追蹤 journalctl -u nginx -f # 只看最近1小時(shí) journalctl -u nginx --since "1 hour ago"
journalctl 是現(xiàn)代Linux的統(tǒng)一日志系統(tǒng),比直接讀文件更強(qiáng)大!
用Java寫(xiě)一個(gè)Nginx日志分析小工具(實(shí)戰(zhàn)?。?/h2>
現(xiàn)在,我們來(lái)點(diǎn)硬核的——用Java寫(xiě)一個(gè)程序,自動(dòng)分析Nginx訪問(wèn)日志,統(tǒng)計(jì)Top 10最慢請(qǐng)求、404統(tǒng)計(jì)、IP訪問(wèn)頻率。
準(zhǔn)備工作
你需要:
- Java 8+ 環(huán)境
- 一個(gè)Nginx訪問(wèn)日志文件(可以是測(cè)試用的)
- Maven項(xiàng)目(可選)
示例日志文件(access.log)
我們創(chuàng)建一個(gè)簡(jiǎn)單的測(cè)試日志文件:
192.168.1.100 - - [25/Apr/2024:10:30:22 +0800] "GET /api/v1/user HTTP/1.1" 200 1234 "-" "Mozilla/5.0" 0.045 0.042 192.168.1.101 - - [25/Apr/2024:10:30:23 +0800] "GET /api/v1/product HTTP/1.1" 404 567 "-" "curl/7.68.0" 0.002 0.001 192.168.1.100 - - [25/Apr/2024:10:30:24 +0800] "GET /api/v1/user HTTP/1.1" 200 1234 "-" "Mozilla/5.0" 0.089 0.085 192.168.1.102 - - [25/Apr/2024:10:30:25 +0800] "GET /admin HTTP/1.1" 404 567 "-" "Mozilla/5.0" 0.001 0.000 192.168.1.103 - - [25/Apr/2024:10:30:26 +0800] "GET /api/v1/order HTTP/1.1" 500 1000 "-" "PostmanRuntime/7.30.0" 2.100 2.050 192.168.1.100 - - [25/Apr/2024:10:30:27 +0800] "GET /api/v1/user HTTP/1.1" 200 1234 "-" "Mozilla/5.0" 0.033 0.030 192.168.1.104 - - [25/Apr/2024:10:30:28 +0800] "GET /favicon.ico HTTP/1.1" 404 567 "-" "Mozilla/5.0" 0.001 0.000 192.168.1.105 - - [25/Apr/2024:10:30:29 +0800] "GET /api/v1/product HTTP/1.1" 404 567 "-" "curl/7.68.0" 0.002 0.001 192.168.1.106 - - [25/Apr/2024:10:30:30 +0800] "POST /login HTTP/1.1" 401 120 "-" "BurpSuite" 0.005 0.004 192.168.1.107 - - [25/Apr/2024:10:30:31 +0800] "GET /api/v1/user HTTP/1.1" 200 1234 "-" "Mozilla/5.0" 0.022 0.020
注意:我們假設(shè)日志格式是 detailed 格式,最后兩列是 $request_time 和 $upstream_response_time。
Java代碼:NginxLogAnalyzer
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class NginxLogAnalyzer {
public static void main(String[] args) {
String logFilePath = "/var/log/nginx/access.log"; // 替換為你的日志路徑
System.out.println("?? Nginx日志分析工具啟動(dòng)中... ??\n");
try {
List<LogEntry> logs = parseLogEntries(logFilePath);
if (logs.isEmpty()) {
System.out.println("?? 未找到有效日志條目,請(qǐng)檢查文件路徑或格式。\n");
return;
}
System.out.println("?? 分析結(jié)果如下:\n");
// 1. 統(tǒng)計(jì)HTTP狀態(tài)碼分布
analyzeStatusCodes(logs);
// 2. 找出最慢的10個(gè)請(qǐng)求
analyzeSlowestRequests(logs);
// 3. 統(tǒng)計(jì)Top 10 IP訪問(wèn)頻率
analyzeTopIPs(logs);
// 4. 統(tǒng)計(jì)404錯(cuò)誤最多的URL
analyzeTop404Urls(logs);
// 5. 檢測(cè)異常行為:同一IP在1秒內(nèi)請(qǐng)求>5次
detectSuspiciousActivity(logs);
} catch (IOException e) {
System.err.println("? 讀取日志文件失敗: " + e.getMessage());
}
}
// 解析每一行日志
static List<LogEntry> parseLogEntries(String filePath) throws IOException {
List<LogEntry> entries = new ArrayList<>();
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
lines.filter(line -> !line.trim().isEmpty())
.forEach(line -> {
try {
LogEntry entry = parseLogLine(line);
if (entry != null) entries.add(entry);
} catch (Exception e) {
// 忽略格式錯(cuò)誤的行
System.err.println("?? 解析失敗行: " + line);
}
});
}
return entries;
}
// 解析單行日志(按空格分割,注意URL可能含空格,所以不能簡(jiǎn)單split)
static LogEntry parseLogLine(String line) {
// 我們假設(shè)格式是:IP - - [時(shí)間] "請(qǐng)求" 狀態(tài) 字節(jié)數(shù) "Referer" "UA" 請(qǐng)求耗時(shí) 上游耗時(shí)
// 用正則更安全,但為簡(jiǎn)化,我們按空格拆分,然后合并請(qǐng)求字段
String[] parts = line.split(" ", 10); // 最多切10份
if (parts.length < 10) return null;
String clientIp = parts[0];
String requestTimeStr = parts[3].substring(1); // 去掉 [
String request = parts[5] + " " + parts[6] + " " + parts[7]; // GET /api/user HTTP/1.1
int status = Integer.parseInt(parts[8]);
long bodyBytes = Long.parseLong(parts[9]);
double requestTime = Double.parseDouble(parts[10]);
double upstreamTime = parts.length > 11 ? Double.parseDouble(parts[11]) : 0.0;
// 提取時(shí)間
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z");
LocalDateTime time = LocalDateTime.parse(requestTimeStr, formatter);
return new LogEntry(
clientIp,
time,
request,
status,
bodyBytes,
requestTime,
upstreamTime
);
}
// 統(tǒng)計(jì)狀態(tài)碼分布
static void analyzeStatusCodes(List<LogEntry> logs) {
Map<Integer, Long> statusCount = logs.stream()
.collect(Collectors.groupingBy(LogEntry::getStatus, Collectors.counting()));
System.out.println("?? HTTP狀態(tài)碼統(tǒng)計(jì):");
statusCount.entrySet().stream()
.sorted(Map.Entry.<Integer, Long>comparingByValue().reversed())
.forEach(e -> System.out.printf(" %d: %d 次%n", e.getKey(), e.getValue()));
System.out.println();
}
// 找出最慢的10個(gè)請(qǐng)求
static void analyzeSlowestRequests(List<LogEntry> logs) {
System.out.println("?? 最慢的10個(gè)請(qǐng)求(按$request_time排序):");
logs.stream()
.sorted(Comparator.comparingDouble(LogEntry::getRequestTime).reversed())
.limit(10)
.forEach(entry -> System.out.printf(
" %.3fs | %d %s | IP: %s%n",
entry.getRequestTime(),
entry.getStatus(),
entry.getRequest(),
entry.getClientIp()
));
System.out.println();
}
// 統(tǒng)計(jì)Top 10 IP訪問(wèn)頻率
static void analyzeTopIPs(List<LogEntry> logs) {
System.out.println("?? Top 10 訪問(wèn)IP:");
logs.stream()
.collect(Collectors.groupingBy(LogEntry::getClientIp, Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(10)
.forEach(e -> System.out.printf(" %s: %d 次%n", e.getKey(), e.getValue()));
System.out.println();
}
// 統(tǒng)計(jì)404錯(cuò)誤最多的URL
static void analyzeTop404Urls(List<LogEntry> logs) {
System.out.println("?? 404錯(cuò)誤最多的URL(前5):");
logs.stream()
.filter(entry -> entry.getStatus() == 404)
.collect(Collectors.groupingBy(LogEntry::getRequest, Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(5)
.forEach(e -> System.out.printf(" %s: %d 次%n", e.getKey(), e.getValue()));
System.out.println();
}
// 檢測(cè)可疑行為:同一IP在1秒內(nèi)請(qǐng)求>5次
static void detectSuspiciousActivity(List<LogEntry> logs) {
System.out.println("?? 可疑行為檢測(cè)(同一IP在1秒內(nèi)>5次請(qǐng)求):");
Map<String, List<LogEntry>> ipGroups = logs.stream()
.collect(Collectors.groupingBy(LogEntry::getClientIp));
ipGroups.entrySet().stream()
.filter(entry -> entry.getValue().size() > 5)
.forEach(entry -> {
String ip = entry.getKey();
List<LogEntry> requests = entry.getValue();
requests.sort(Comparator.comparing(LogEntry::getTime));
// 檢查是否存在連續(xù)5個(gè)請(qǐng)求在1秒內(nèi)
for (int i = 0; i <= requests.size() - 5; i++) {
LocalDateTime start = requests.get(i).getTime();
LocalDateTime end = requests.get(i + 4).getTime();
long diffSeconds = java.time.Duration.between(start, end).getSeconds();
if (diffSeconds <= 1) {
System.out.printf(" ?? IP: %s 在 %s 到 %s 間發(fā)起 %d 次請(qǐng)求(%d秒內(nèi))%n",
ip, start, end, 5, diffSeconds);
break;
}
}
});
System.out.println();
}
// 日志條目模型
static class LogEntry {
private final String clientIp;
private final LocalDateTime time;
private final String request;
private final int status;
private final long bodyBytes;
private final double requestTime;
private final double upstreamTime;
public LogEntry(String clientIp, LocalDateTime time, String request, int status,
long bodyBytes, double requestTime, double upstreamTime) {
this.clientIp = clientIp;
this.time = time;
this.request = request;
this.status = status;
this.bodyBytes = bodyBytes;
this.requestTime = requestTime;
this.upstreamTime = upstreamTime;
}
// Getters
public String getClientIp() { return clientIp; }
public LocalDateTime getTime() { return time; }
public String getRequest() { return request; }
public int getStatus() { return status; }
public long getBodyBytes() { return bodyBytes; }
public double getRequestTime() { return requestTime; }
public double getUpstreamTime() { return upstreamTime; }
}
}運(yùn)行效果示例
假設(shè)你運(yùn)行上面的程序,輸入我們上面的測(cè)試日志,輸出如下:
?? Nginx日志分析工具啟動(dòng)中... ??
?? 分析結(jié)果如下:
?? HTTP狀態(tài)碼統(tǒng)計(jì):
200: 5 次
404: 4 次
401: 1 次
500: 1 次
?? 最慢的10個(gè)請(qǐng)求(按$request_time排序):
2.100s | 500 GET /api/v1/order HTTP/1.1 | IP: 192.168.1.103
0.089s | 200 GET /api/v1/user HTTP/1.1 | IP: 192.168.1.100
0.045s | 200 GET /api/v1/user HTTP/1.1 | IP: 192.168.1.100
0.033s | 200 GET /api/v1/user HTTP/1.1 | IP: 192.168.1.100
0.022s | 200 GET /api/v1/user HTTP/1.1 | IP: 192.168.1.107
0.005s | 401 POST /login HTTP/1.1 | IP: 192.168.1.106
0.002s | 404 GET /api/v1/product HTTP/1.1 | IP: 192.168.1.101
0.002s | 404 GET /api/v1/product HTTP/1.1 | IP: 192.168.1.105
0.001s | 404 GET /admin HTTP/1.1 | IP: 192.168.1.102
0.001s | 404 GET /favicon.ico HTTP/1.1 | IP: 192.168.1.104
?? Top 10 訪問(wèn)IP:
192.168.1.100: 4 次
192.168.1.101: 1 次
192.168.1.102: 1 次
192.168.1.103: 1 次
192.168.1.104: 1 次
192.168.1.105: 1 次
192.168.1.106: 1 次
192.168.1.107: 1 次
?? 404錯(cuò)誤最多的URL(前5):
GET /api/v1/product HTTP/1.1: 2 次
GET /admin HTTP/1.1: 1 次
GET /favicon.ico HTTP/1.1: 1 次
?? 可疑行為檢測(cè)(同一IP在1秒內(nèi)>5次請(qǐng)求):
(無(wú)輸出,說(shuō)明沒(méi)有異常)
你看到了嗎?
- 有一個(gè)請(qǐng)求耗時(shí)2.1秒 → 后端Java服務(wù)可能慢了!
/api/v1/product被頻繁請(qǐng)求404 → 接口不存在,可能是前端配置錯(cuò)誤192.168.1.100是高頻訪問(wèn)者 → 可能是爬蟲(chóng)或內(nèi)部測(cè)試工具
用Mermaid畫(huà)日志分析流程圖(可視化你的思維)
我們來(lái)用Mermaid畫(huà)一個(gè)Nginx日志分析流程圖,幫你理清思路:

這張圖不是裝飾品,它是你排查問(wèn)題的思維導(dǎo)圖。
每次遇到問(wèn)題,先對(duì)照這張圖,一步步走下來(lái),不會(huì)漏掉關(guān)鍵點(diǎn)。
高頻問(wèn)題排查實(shí)戰(zhàn):5個(gè)真實(shí)場(chǎng)景
場(chǎng)景1:用戶說(shuō)“網(wǎng)站打不開(kāi)”,但Nginx進(jìn)程是running的
現(xiàn)象:瀏覽器顯示 502 Bad Gateway
排查步驟:
- 查看
error.log:
tail -f /var/log/nginx/error.log
- 看到:
connect() failed (111: Connection refused) while connecting to upstream
- 說(shuō)明:Nginx想轉(zhuǎn)發(fā)請(qǐng)求給后端(如Java),但后端沒(méi)在監(jiān)聽(tīng)。
- 解決:
# 檢查Java應(yīng)用是否啟動(dòng) ps aux | grep java # 檢查端口是否監(jiān)聽(tīng) netstat -tlnp | grep 8080 # 或者用 ss(推薦) ss -tlnp | grep 8080 # 如果沒(méi)監(jiān)聽(tīng) → 啟動(dòng)Java應(yīng)用 nohup java -jar your-app.jar > app.log 2>&1 &
你也可以用 curl http://localhost:8080/health 測(cè)試后端是否正常。
場(chǎng)景2:大量404錯(cuò)誤,用戶訪問(wèn)了不存在的頁(yè)面
現(xiàn)象:access.log 中出現(xiàn)大量 GET /wp-admin、GET /robots.txt、GET /api/v1/user/123456(但API不存在)
排查:
grep " 404 " /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -20
發(fā)現(xiàn):
1200 /wp-admin 800 /phpmyadmin 500 /api/v1/user/123456
解決:
- 如果是惡意掃描:用
fail2ban封禁IP - 如果是前端路由錯(cuò)誤:檢查前端代碼是否拼錯(cuò)了URL
- 如果是API被暴力探測(cè):加JWT認(rèn)證、限流、或返回403而不是404(防信息泄露)
安全建議:不要在404頁(yè)面暴露系統(tǒng)結(jié)構(gòu)。返回統(tǒng)一的“頁(yè)面不存在”即可。
場(chǎng)景3:頁(yè)面加載慢,但后端Java服務(wù)響應(yīng)很快
現(xiàn)象:access.log 中 $request_time = 3s,但 $upstream_response_time = 0.01s
分析:
- Nginx處理耗時(shí)3秒,但Java只用了0.01秒 → 問(wèn)題在Nginx層!
可能原因:
- Nginx配置了
proxy_buffering off;,導(dǎo)致大文件傳輸慢 proxy_read_timeout設(shè)置太短,Nginx等待超時(shí)重試- SSL握手慢(證書(shū)鏈太長(zhǎng))
- 靜態(tài)資源未緩存,每次都要從磁盤(pán)讀
解決:
# 啟用緩沖
proxy_buffering on;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
# 增加超時(shí)
proxy_read_timeout 60s;
proxy_connect_timeout 60s;
# 緩存靜態(tài)資源
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
場(chǎng)景4:同一個(gè)IP在1秒內(nèi)請(qǐng)求100次
現(xiàn)象:access.log 中某IP連續(xù)請(qǐng)求 /api/login,狀態(tài)碼全是401
分析:
- 很可能是密碼爆破攻擊!
解決:
- 用
fail2ban自動(dòng)封禁IP:
sudo apt install fail2ban sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
編輯 /etc/fail2ban/jail.local:
[nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 3 bantime = 3600
重啟:
sudo systemctl restart fail2ban
- 或者用Nginx內(nèi)置限流:
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
server {
location /api/login {
limit_req zone=login_limit burst=5 nodelay;
proxy_pass http://backend;
}
}
場(chǎng)景5:日志文件太大,磁盤(pán)快滿了!
現(xiàn)象:df -h 顯示 /var 使用率 98%
解決:
- 清理舊日志:
# 查看日志大小 ls -lh /var/log/nginx/ # 清空日志(保留文件,清空內(nèi)容) > /var/log/nginx/access.log # 或者壓縮歸檔 gzip /var/log/nginx/access.log mv /var/log/nginx/access.log.gz /var/log/nginx/access.log.20240425.gz
- 配置日志輪轉(zhuǎn)(logrotate)
編輯 /etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
這樣,Nginx每天自動(dòng)輪轉(zhuǎn)日志,保留14天,自動(dòng)壓縮,不會(huì)撐爆磁盤(pán)。
生產(chǎn)環(huán)境必須配置logrotate!
自動(dòng)化:用Shell腳本每天發(fā)日志摘要郵件
你不想每天登錄服務(wù)器看日志?我們可以寫(xiě)個(gè)腳本,每天早上8點(diǎn)自動(dòng)發(fā)一封郵件給你!
#!/bin/bash
# /usr/local/bin/nginx-daily-report.sh
LOG_FILE="/var/log/nginx/access.log"
REPORT_FILE="/tmp/nginx-report-$(date +%Y%m%d).txt"
echo "?? Nginx日志日?qǐng)?bào) - $(date)" > $REPORT_FILE
echo "=========================================" >> $REPORT_FILE
# 統(tǒng)計(jì)總請(qǐng)求數(shù)
TOTAL=$(wc -l < $LOG_FILE)
echo "? 總請(qǐng)求數(shù): $TOTAL" >> $REPORT_FILE
# 統(tǒng)計(jì)404
ERROR_404=$(grep " 404 " $LOG_FILE | wc -l)
echo "? 404錯(cuò)誤: $ERROR_404" >> $REPORT_FILE
# 最慢的3個(gè)請(qǐng)求
echo -e "\n?? 最慢的3個(gè)請(qǐng)求:" >> $REPORT_FILE
awk '{print $10, $7}' $LOG_FILE | sort -nr | head -3 >> $REPORT_FILE
# 發(fā)送郵件(需配置sendmail或postfix)
mail -s "Nginx日志日?qǐng)?bào) $(date +%Y-%m-%d)" your-email@example.com < $REPORT_FILE
# 清理
rm $REPORT_FILE
然后加到crontab:
crontab -e
添加:
0 8 * * * /usr/local/bin/nginx-daily-report.sh
每天早上8點(diǎn),你的郵箱就會(huì)收到一份Nginx日志摘要!
企業(yè)級(jí)做法:用ELK(Elasticsearch + Logstash + Kibana)或Grafana Loki做可視化日志平臺(tái)。
# Ubuntu安裝GoAccess sudo apt install goaccess # 實(shí)時(shí)分析Nginx日志 goaccess /var/log/nginx/access.log --log-format=COMBINED -a
你會(huì)看到一個(gè)彩色終端儀表盤(pán),實(shí)時(shí)顯示:訪問(wèn)量、來(lái)源、瀏覽器、操作系統(tǒng)、地理分布!
總結(jié):小白到高手的Nginx日志成長(zhǎng)路徑
| 階段 | 你該掌握的技能 |
|---|---|
| ?? 小白 | 知道日志在哪,會(huì)用 tail -f |
| ?? 初級(jí) | 會(huì)用 grep、awk、sort 做簡(jiǎn)單統(tǒng)計(jì) |
| ?? 中級(jí) | 會(huì)寫(xiě)Java/Python腳本自動(dòng)化分析,識(shí)別404、慢請(qǐng)求 |
| ?? 高級(jí) | 會(huì)配置logrotate、fail2ban、限流、自定義日志格式 |
| ??♂? 大神 | 會(huì)搭建ELK、寫(xiě)Prometheus監(jiān)控、做日志告警、優(yōu)化Nginx性能 |
最后的話:日志是你的戰(zhàn)友,不是敵人
很多開(kāi)發(fā)者討厭看日志,覺(jué)得它“枯燥”、“亂糟糟”、“看不懂”。
但真正厲害的工程師,把日志當(dāng)小說(shuō)讀。
每一個(gè) 404,都是一個(gè)用戶找不到入口的嘆息;
每一個(gè) 502,都是后端服務(wù)在喘息;
每一個(gè) 200,都是系統(tǒng)在默默為你服務(wù)。
你讀得越多,就越懂你的系統(tǒng)。
不要等到線上崩潰才去看日志。
每天花5分鐘,看看Nginx日志,你會(huì)成為團(tuán)隊(duì)里最靠譜的那個(gè)人。
附錄:常用Nginx日志變量速查表
| 變量名 | 含義 | 示例 |
|---|---|---|
$remote_addr | 客戶端IP | 192.168.1.100 |
$time_local | 本地時(shí)間 | [25/Apr/2024:10:30:22 +0800] |
$request | 請(qǐng)求行 | GET /api/user HTTP/1.1 |
$status | HTTP狀態(tài)碼 | 200 |
$body_bytes_sent | 響應(yīng)體大?。ㄗ止?jié)) | 1234 |
$http_referer | 來(lái)源頁(yè)面 | https://google.com |
$http_user_agent | 用戶代理 | Mozilla/5.0 Chrome/123 |
$request_time | Nginx處理耗時(shí) | 0.045 |
$upstream_response_time | 后端響應(yīng)耗時(shí) | 0.042 |
$upstream_status | 后端返回狀態(tài)碼 | 200 |
$http_x_forwarded_for | 代理鏈原始IP | 192.168.1.1, 10.0.0.1 |
結(jié)語(yǔ):你,已經(jīng)不是那個(gè)怕看日志的小白了
今天,你學(xué)會(huì)了:
- ? Nginx日志文件的默認(rèn)位置
- ? 訪問(wèn)日志 vs 錯(cuò)誤日志的區(qū)別與價(jià)值
- ? 如何用 Linux命令 快速分析日志
- ? 如何用 Java 寫(xiě)一個(gè)日志分析工具
- ? 如何用 Mermaid 畫(huà)出分析流程圖
- ? 5個(gè)真實(shí)生產(chǎn)場(chǎng)景的排查方法
- ? 如何用 Shell腳本 自動(dòng)發(fā)日?qǐng)?bào)
- ? 如何用 GoAccess 做可視化分析
你已經(jīng)超越了80%的程序員。
現(xiàn)在,打開(kāi)你的終端,運(yùn)行:
tail -f /var/log/nginx/access.log
然后,靜靜地看著那些字符流動(dòng)——
那是你系統(tǒng)的心跳,
是你服務(wù)的生命線。
你,現(xiàn)在是一個(gè)能讀懂日志的人了。
記?。赫嬲募夹g(shù),不是寫(xiě)多少行代碼,而是能看懂系統(tǒng)在說(shuō)什么。
以上就是Nginx日志文件的位置與基礎(chǔ)查看指南的詳細(xì)內(nèi)容,更多關(guān)于Nginx日志文件位置與查看的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Nginx服務(wù)器中關(guān)于SSL的安全配置詳解
這篇文章主要介紹了Nginx服務(wù)器中關(guān)于SSL的安全配置詳解,2014年曝出的SSL安全漏洞無(wú)疑為整個(gè)業(yè)界帶來(lái)了巨大震動(dòng),本文便對(duì)此給出相關(guān)安全維護(hù)方法,需要的朋友可以參考下2015-06-06
nginx搭建IPv6 ->IPv4反向代理服務(wù)器的實(shí)現(xiàn)
本文主要介紹了nginx搭建IPv6 ->IPv4反向代理服務(wù)器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-05-05
nginx 如何實(shí)現(xiàn)if嵌套的方法示例
這篇文章主要介紹了nginx 如何實(shí)現(xiàn)if嵌套的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Nginx實(shí)現(xiàn)動(dòng)態(tài)攔截非法訪問(wèn)ip的方法
最近在訪問(wèn)時(shí)不時(shí)會(huì)被暴力刷量,爬蟲(chóng)和惡意攻擊導(dǎo)致數(shù)據(jù)庫(kù),服務(wù)等癱瘓,所以本文介紹了在Nginx上實(shí)現(xiàn)一個(gè)動(dòng)態(tài)攔截IP的方法,具體是當(dāng)某個(gè)IP在1分鐘內(nèi)訪問(wèn)超過(guò)60次時(shí),將其加入Redis并攔截,攔截時(shí)間默認(rèn)1天,需要的朋友可以參考下2025-02-02
nginx關(guān)閉/重啟/啟動(dòng)的操作方法
在本篇文章里小編給各位分享的是關(guān)于nginx關(guān)閉/重啟/啟動(dòng)的操作方法,有興趣的朋友們可以學(xué)習(xí)參考下。2020-03-03

