你是否曾對(duì)著空白的終端發(fā)呆,不知道從哪里下手排查問(wèn)題?別慌——你不是一個(gè)人,本文將從零開(kāi)始,徹底搞懂Nginx日志的位置、結(jié)構(gòu)、查看方式、分析技巧" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Nginx日志文件的位置與基礎(chǔ)查看指南

 更新時(shí)間:2026年07月07日 09:27:23   作者:知遠(yuǎn)漫談  
你是否曾在服務(wù)器上遇到網(wǎng)站打不開(kāi)、502 Bad Gateway、404 Not Found這類令人抓狂的錯(cuò)誤?
你是否曾對(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"
$statusHTTP響應(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 deniedNginx無(wú)權(quán)讀取文件或連接socketchown -R www-data:www-data /var/www/html
SSL_do_handshake() failedSSL證書(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ì)看到新日志!

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

為什么是 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

排查步驟

  1. 查看 error.log
tail -f /var/log/nginx/error.log
  1. 看到:
connect() failed (111: Connection refused) while connecting to upstream
  1. 說(shuō)明:Nginx想轉(zhuǎn)發(fā)請(qǐng)求給后端(如Java),但后端沒(méi)在監(jiān)聽(tīng)。
  2. 解決:
# 檢查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-adminGET /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

分析

  • 很可能是密碼爆破攻擊!

解決

  1. 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
  1. 或者用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%

解決

  1. 清理舊日志
# 查看日志大小
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
  1. 配置日志輪轉(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、awksort 做簡(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客戶端IP192.168.1.100
$time_local本地時(shí)間[25/Apr/2024:10:30:22 +0800]
$request請(qǐng)求行GET /api/user HTTP/1.1
$statusHTTP狀態(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_timeNginx處理耗時(shí)0.045
$upstream_response_time后端響應(yīng)耗時(shí)0.042
$upstream_status后端返回狀態(tài)碼200
$http_x_forwarded_for代理鏈原始IP192.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四種調(diào)度算法和進(jìn)階

    深入淺析nginx四種調(diào)度算法和進(jìn)階

    這篇文章主要介紹了深入淺析nginx四種調(diào)度算法和進(jìn)階,文中給大家提到了upstream 支持4種負(fù)載均衡調(diào)度算法,需要的朋友可以參考下
    2018-06-06
  • 前端必備Nginx配置詳解

    前端必備Nginx配置詳解

    這篇文章主要介紹了前端必備Nginx配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Nginx服務(wù)器中關(guān)于SSL的安全配置詳解

    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配置UDP代理的實(shí)現(xiàn)

    Nginx配置UDP代理的實(shí)現(xiàn)

    使用Nginx作為UDP代理可以幫助您實(shí)現(xiàn)數(shù)據(jù)包的轉(zhuǎn)發(fā)和負(fù)載均衡,本文就詳細(xì)的來(lái)介紹一下Nginx配置UDP代理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • nginx搭建IPv6 ->IPv4反向代理服務(wù)器的實(shí)現(xiàn)

    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嵌套的方法示例

    這篇文章主要介紹了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的方法

    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實(shí)現(xiàn)流量控制的示例代碼

    Nginx實(shí)現(xiàn)流量控制的示例代碼

    本文主要介紹了Nginx實(shí)現(xiàn)流量控制的示例代碼,可以用來(lái)限制用戶在給定時(shí)間內(nèi)HTTP請(qǐng)求的數(shù)量,保護(hù)服務(wù)器,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • nginx關(guān)閉/重啟/啟動(dòng)的操作方法

    nginx關(guān)閉/重啟/啟動(dòng)的操作方法

    在本篇文章里小編給各位分享的是關(guān)于nginx關(guān)閉/重啟/啟動(dòng)的操作方法,有興趣的朋友們可以學(xué)習(xí)參考下。
    2020-03-03
  • 使用Nginx來(lái)共享文件的詳細(xì)教程

    使用Nginx來(lái)共享文件的詳細(xì)教程

    有時(shí)我們想共享電腦上的某些文件,一個(gè)比較方便的做法是,開(kāi)一個(gè)HTTP服務(wù),指向文件所在的目錄,這次我們用 nginx 來(lái)實(shí)現(xiàn)這個(gè)需求,本文將通過(guò)代碼示例一步步教你使用Nginx來(lái)共享文件,需要的朋友可以參考下
    2025-01-01

最新評(píng)論

海安县| 澄城县| 西充县| 宁都县| 宣威市| 红原县| 纳雍县| 墨玉县| 伊宁县| 杭州市| 诸暨市| 襄樊市| 宁阳县| 霍邱县| 星子县| 章丘市| 吉安县| 玉屏| 个旧市| 梁河县| 闽清县| 桦甸市| 尼玛县| 临沭县| 伊春市| 平利县| 武陟县| 阜平县| 新兴县| 双牌县| 乌兰察布市| 东城区| 东兴市| 康保县| 盱眙县| 临武县| 马龙县| 道真| 拜泉县| 通海县| 奇台县|