ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Nginx日志分析实战:用Linux命令把死数据变成活情报

Nginx日志分析实战:用Linux命令把死数据变成活情报 很多人总在群里问我Nginx日志几百MB甚至几个GB每次排查都要用编辑器打开慢慢翻或者干脆下到本地用Excel处理卡得眼睛都快瞎了。其实在Linux服务器上几条看似不起眼的命令组合起来就能把日志文件里那些“死数据”变成可以直接指导调优和排查的“活情报”。今天我就把平时用得最多的一套Nginx系统日志分析思路和命令整理出来从最基础的命令语法讲到实战场景全部是我自己踩过坑之后的经验希望帮你少走点弯路。1. 日志分析前得先明白Nginx到底记了什么1.1 日志文件里藏着一个网站的完整访问画像所谓“分析Nginx日志”本质上就是从一个文本文件里把用户访问的时间、来源、路径、状态、流量这些信息提取出来再按维度聚合统计。Nginx默认会在/var/log/nginx/下生成两个关键文件access.log记录每一次HTTP请求error.log记录错误、崩溃、配置异常等信息。access.log是我们日常排查的重头戏因为每一次请求的来龙去脉都在里面。不过很多初学者拿到日志就会犯一个错误直接把整个文件从头看到尾。这基本是在做无用功。日志文件的每一行是一个独立的请求字段之间有空格分隔但某些字段内部又包含引号和空格比如请求的User-AgentUA里必然带空格和引号。所以分析的时候不能用简单的cut -d 一刀切而是要基于Nginx默认的log_format规则理解每一列的含义再选用合适的命令去切割和提取。1.2 先看明白默认日志格式后续命令才不会出错我在生产环境最常见的Nginx配置是这样的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;对应的日志一行大致长这样203.0.113.10 - - [15/May/2025:14:23:11 0800] GET /api/users?id123 HTTP/1.1 200 1024 https://example.com/home Mozilla/5.0 (Windows NT 10.0; Win64; x64) 203.0.113.10拆开看就是remote_addr访客IP可能是真实IP也可能是CDN节点IP。remote_userHTTP Basic认证用户名没有就是-。time_local访问时间带时区。request完整的请求行包括方法、URI、协议。statusHTTP状态码200、404、500这些。body_bytes_sent响应给客户端的字节数不含响应头。http_referer来源页面。http_user_agent客户端UA也就是浏览器或爬虫标识。http_x_forwarded_for经过代理链时记录的原始IP可自己配置。搞清楚这个格式就能明白为什么后面所有统计命令都用awk而不是简单cut。因为$request和$http_user_agent字段内部有空格和引号awk默认按连续空白分割如果正则和分隔符没写对取出来的字段就全乱了。提示如果你的Nginx配置没有自定义log_format默认情况下日志格式是combined字段顺序和上面差不多但缺了$http_x_forwarded_for。建议上线前就检查一下log_format这能省掉后面很多分析时的麻烦。2. 高频实用命令查看、过滤与初步统计2.1 从头部看趋势用head和tail快速锁定范围拿到日志文件我一般不会直接统计全部数据而是先“摸个底”。看文件多大有多少行前几行是什么样的最后几行是什么时候的。这一步只需要几条命令ls -lh /var/log/nginx/access.log wc -l /var/log/nginx/access.log head -n 5 /var/log/nginx/access.log tail -n 20 /var/log/nginx/access.logls -lh能让我判断这个日志是不是已经膨胀到了该轮转的规模wc -l知道总请求数head和tail看首尾时间戳能快速确认日志时间段是否覆盖我要排查的故障时间。有些时候服务器时间不对或者日志被中途清空过这一步就能发现异常。如果线上正在出现故障要实时盯着日志刷新我会用tail -f配合grep做关键字过滤。比如只看500错误tail -f /var/log/nginx/access.log | grep 500 这里有讲究grep匹配的时候最好带前后空格避免把状态码500和其他字段里的500请求时间、字节数误伤。比如500可能匹配到15005这种数值加空格能减少误匹配。2.2 用grep和正则精准过滤把“噪音”丢掉日志分析的一大难点是噪音数据。爬虫、探测请求、静态资源请求、监控探活请求会把真正有价值的用户请求淹没。我常用的几个grep模式记录一下# 排除健康检查请求 grep -v /healthcheck access.log # 提取POST接口相关请求 grep POST access.log # 提取某个IP所有请求 grep 203.0.113.10 access.log # 提取UA中含有Googlebot的请求 grep Googlebot access.log # 提取所有4xx/5xx错误 grep -E HTTP/1\.1 (4[0-9][0-9]|5[0-9][0-9]) access.loggrep -E是扩展正则(4[0-9][0-9]|5[0-9][0-9])匹配400到599的状态码。注意这里匹配的是状态码数字前后有空格的样子所以模式里要带上HTTP/1.1后面的空格。实际使用中我会把爬虫这类“噪音”先排除掉比如分析访问量时屏蔽常见爬虫UA和监控IP。毕竟不是所有流量都与业务有关。2.3 用awk做字段级提取一切统计的基础awk是我分析Nginx日志用得最多的命令没有之一。前面说过日志按空格分列$1是IP$4是时间$6是请求方法$7是请求URI$9是状态码$10是返回字节数。注意这几个编号是因为中间有引号和时间括号的影响实际在awk的默认分隔符下$4是[15/May/2025:14:23:11$5是0800]所以时间要做修剪。一个最基础的按IP统计请求量的命令awk {print $1} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 20这条命令的思路是awk取出每一行的IP字段sort排序uniq -c统计每个IP出现的次数再sort -rn按次数降序排列最后head取出前20。为什么中间要sort因为uniq只统计相邻的相同行不排序就会统计错误。同样的套路可以套到任意字段上。统计最热门的URIawk {print $7} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 20统计状态码分布awk {print $9} /var/log/nginx/access.log | sort | uniq -c | sort -rn如果你想搞懂每行各字段编号是什么可以先跑一个awk {print NR, NF, $1, $4, $7, $9} access.log | head -5看NR是行号、NF是字段数量确认自己的Nginx版本和日志格式是否和预期一致。3. 实战三个最常见的分析场景3.1 统计PV、UV、独立IP和热门URLPV综合浏览量就是日志总行数UV则需要按IP去重。假设日志格式标准且没有代理透传IP干扰我一般这样算UVawk {print $1} /var/log/nginx/access.log | sort -u | wc -l这里sort -u先排序再去重wc -l统计唯一IP数量。注意这个指标不等于真实用户数因为局域网内多人可能共享出口IP而且如果前面挂了CDN所有的UV都会变成CDN节点IP必须配合$http_x_forwarded_for字段做二次处理。真实环境我一般先判断日志里有没有x_forwarded_for有的话用最后一个非-的IP作为真实IP去统计awk {if ($NF ! -) ip$NF; else ip$1; print ip} access.log | sort -u | wc -l这里$NF是最后一个字段。但要注意如果经过了多层代理$NF也不一定是真实用户IP具体要看链路怎么签字。很多运维直接取第一个转发IP但有时会被人伪造。这个只能结合业务架构取舍。热门URL统计和PV统计套路一样无非是取$7然后排序计数。但如果URL后面带参数比如/api/users?id123和/api/users?id456想统计接口热度时应该把参数去掉。我用awk内置的substr或者用sed切割awk {print $7} access.log | sed s/?.*// | sort | uniq -c | sort -rn | head这样/api/users?id123会变成/api/users接口维度的统计就准了。3.2 排查错误请求状态码4xx、5xx的来源与集中路径出现大量500时大家第一反应是看后端有没有报错但Nginx日志能告诉你这些错误都打在哪些接口上哪些IP在触发错误。我的标准操作是先统计错误码的分布再定位到具体URI最后回溯IP和UA。# 各状态码分布 awk {print $9} access.log | sort | uniq -c | sort -rn # 只看5xx的URI排名 awk $9 ~ /^5[0-9][0-9]/ {print $7} access.log | sort | uniq -c | sort -rn | head -n 20 # 500错误都由哪些IP触发 awk $9 500 {print $1} access.log | sort | uniq -c | sort -rn这里awk的$9 ~ /^5[0-9][0-9]/表示第9字段匹配以5开头、后面两位数字的字符串本质是正则匹配状态码。如果想把404和500混在一起看就用$9 400但要注意状态码是字符串时比较的是字符串顺序数值上没问题因为都是三位数。另一个常见排查点某个IP频繁触发403或444。Nginx的444是特殊状态码表示直接断连不返回响应。如果日志里大量出现444大概率是恶意扫描。这时候我可以用下面的命令快速找出IPawk $9 444 {print $1} access.log | sort | uniq -c | sort -rn | head然后针对这个IP去查具体请求了哪些路径判断是扫描器还是误伤grep IP地址 access.log | awk {print $7} | sort | uniq -c | sort -rn | head3.3 慢请求分析用响应时间字段定位性能瓶颈默认的combined格式日志里没有记录每个请求的处理耗时。想分析慢请求需要在log_format里加上$request_time字段这个变量是Nginx从收到请求到响应完成的总耗时包括上游处理时间单位是秒。强烈建议生产环境加上这个字段没有的话后续性能分析全凭猜。加了$request_time之后一般在$status后面或者最后加上$request_time。例如log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for $request_time;日志行末尾会多一个浮点数。此时找最慢的请求就可以这样# 按请求耗时排倒序取前20 awk {print $NF, $7, $9, $1} access.log | sort -rn | head -n 20这里$NF因为$request_time放在最后正好就是耗时秒数。sort -rn按数字倒序排能瞬间把最慢的那批请求暴露出来。如果只想分析超过3秒的慢请求awk $NF 3 {print $NF, $7, $9, $1} access.log | sort -rn | head拿到慢请求的URI后就可以去对应的上游服务日志里继续深挖数据库慢查询或外部接口超时。这个组合非常实用我在定位接口割裂、数据库连接池满之类问题时全靠这条命令先圈定范围。4. 日志切分、辅助工具与脚本化复用4.1 别让日志无限膨胀logrotate轮转与定时切割分析日志的前提是日志文件结构完整但生产环境如果一直往同一个access.log里写不用多久就能把磁盘打满而且后续分析也会因为文件过大而变慢。Linux自带的logrotate就是干这个的。默认Nginx安装后会有一个/etc/logrotate.d/nginx配置文件常见内容如下/var/log/nginx/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 nginx adm sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 cat /var/run/nginx.pid fi endscript }要点是daily表示按天轮转rotate 14保留14份compress表示对旧日志压缩delaycompress表示延迟一天压缩保证昨天的日志仍是明文方便快速查询。postrotate脚本里的kill -USR1是让Nginx重新打开日志文件如果不做这一步Nginx会继续往已经被rename的旧文件里写轮转等于白做。手动切割某一天的日志时我常用下面这种方式mv /var/log/nginx/access.log /var/log/nginx/access.log.$(date %Y%m%d) kill -USR1 $(cat /var/run/nginx.pid)这样新请求会写入新建的access.log旧日志被保留在带日期的文件里后续分析历史数据直接按文件名取即可。4.2 组合分析把多个时间段、多个字段综合起来日志轮转后会产生大量历史文件但有时候分析单一文件不够比如要看一周内的PV趋势就需要把一周的日志concat起来再统计。cat access.log.2025051{1..7} access.log | awk {print $1} | sort | uniq -c | sort -rn | headcat可以同时读取多个文件大括号扩展{1..7}是生成连续数字的便捷写法不过如果文件名不规律就直接在命令里列出所有文件。如果日志已经是压缩后的.gz需要用zcat来解压查看zcat access.log.20250515.gz | awk {print $1} | sort | uniq -c | sort -rn | headzcat把压缩文件内容在终端输出跟cat用法一样后面照样可以接管道。注意如果gzip文件特别大zcat会占一定内存和CPU不建议一次解压太多文件。4.3 把高频命令写进shell脚本形成自己的工具集命令行虽然灵活但每次重复敲一长串总会出错。我习惯把常用分析场景抽象成几个小脚本放在/usr/local/bin/下方便随时调用。比如一个统计Top IP的脚本#!/bin/bash logfile${1:-/var/log/nginx/access.log} topn${2:-20} awk {print $1} $logfile | sort | uniq -c | sort -rn | head -n $topn保存为topip加执行权限后我只要敲topip就能看当前日志的Top IP敲topip /data/log/nginx/20250515.log 50就能看指定文件的前50个IP。类似的脚本我还会写topurl、errorstat、slowreq本质都是把前面介绍的awk、sort、uniq组合封装成带参数的函数。如果业务规模再大一点直接把日志接入Elasticsearch或者Loki用Kibana或Grafana做可视化那是另一套体系了。但中小团队要快速定位问题命令行脚本仍然是效率最高的方式启动快、无需额外组件、服务器上开箱即用。5. 常见坑与经验心得5.1 我踩过的五个典型坑第一个坑是日志字段错位。有一次我写统计状态码的命令结果统计出来的“状态码”全是乱码纠结了很久发现上游同事改了日志格式在log_format里多加了一个$ssl_protocol字段整个列就偏移了。所以我现在的习惯是分析前先head -3看真实日志结构再按实际字段编号写awk。第二个坑是grep正则没有锚定误匹配严重。比如搜500时把http_code500、请求参数里的500全部带出来了统计结果虚高。后来统一要求自己用grep -E HTTP/1\.[01] 500 这种带上下文的匹配或者干脆用awk的$9 500。第三个坑是unistat前忘了sort。刚开始学的时候我直接awk {print $1} access.log | uniq -c | sort -rn统计出来的次数全不对因为uniq只合并连续重复行。这条命令虽然已经在网上被写烂了但实际还会有人犯包括曾经的我自己。第四个坑是大文件统计时内存不够。有一次线上日志一个文件接近8GB我直接cat整份加sort结果sort阶段把内存和swap吃满差点把业务搞挂。后来我学会了用awk初步聚合再排序或者按小时分文件统计再合并。比如直接awk {ip[$1]} END{for(i in ip) print ip[i], i} access.log | sort -rn | head用awk内建数组做聚合相比先sort再uniq可以减少中间过程的数据量。第五个坑是忽略x_forwarded_for导致IP统计失真。前面说过只要有CDN或LB日志里的$remote_addr就是代理节点IP不是用户IP。如果没意识到这一点你把代理IP当用户IP去封禁很可能把所有用户都封了。所以凡是上线了CDN的站点log_format里务必加上$http_x_forwarded_for而且要认真定义“真实IP”的取值逻辑。5.2 我的几个分析习惯先说一个根深蒂固的习惯任何数据分析前先“抽样验证”。我会先拿tail -n 1000的日志跑一遍统计命令把结果跟手工抽查的几条记录对比确认字段选取正确再跑全量。这样做能避免因为格式变化导致全量统计作废。再说一个日常巡检的技巧我会每天凌晨用cron跑一个简单的统计脚本把昨天的请求量、错误量、Top IP、Top URL追加到一个文本报告里同时如果错误率超过阈值就发告警。用到的命令还是前面那些只是写进了cron任务0 1 * * * /usr/local/bin/nginx_report.sh /var/log/nginx_report.log 21最后说说对“分析”这件事的理解。Nginx日志分析不是跑几条命令就完事而是要有目的性地提问你想知道今天谁访问最多还是哪个接口响应最慢还是什么攻击在打你的站点带着问题去挑命令比背一堆命令更有用。像我前面给的场景都是实际运维中反复出现的高频问题你把这几类套路装进脑子里大部分日志排查都能顺手解决。如果你刚开始接触这些命令不用急着背参数先拿自己服务器的日志多跑几遍看看输出长什么样。跑错几次没关系把字段弄明白、把管道顺序理清楚后面就越来越顺。Nginx日志分析这个技能用起来之后你会发现它就是Linux运维工具箱里最不值钱、却最不能缺的那一类基本功。
返回列表