如何统计Nginx日志中访问量最高的Top N个URL?

来源:JS教程作者:美园和花头衔:网络博主
导读:本期聚焦于美园和花创作的《如何统计Nginx日志中访问量最高的Top N个URL?》,敬请观看详情。想知道线上哪些接口最常被访问,其实不用立刻搭建复杂的数据分析平台,Nginx 的访问日志本身就能给出答案。日志中的 request 字段包含请求方法、URL 和协议版本,通过提取 URL、按出现次数分组计数再排序,就能得到 Top N 排行。但在实际落地时,直接统计原始日志会遇到几个坑:查询参数会让同一个接口分裂成多个条目,静态资源会占据榜单,健康检查和状态码异常的请求也会干扰结果。本文围绕 Nginx 日志格式、awk 与 sort 命令组合、查询参数处理、状态码过滤、Python 脚本以及 GoAccess 等工具展开,给出可直接使用的命令和脚本,帮助你快速定位高频接口、分析流量热点。文中的示例均可在 Linux 服务器上直接运行,适合日常排查和容量评估。

统计 Nginx 日志中 URL 的访问量排行,是接口治理、性能优化和容量评估中非常基础的一项工作。Nginx 的 access_log 会按照配置好的格式记录每一次请求,其中 request 字段完整保存了请求方法、URL 和协议版本。只要把这个字段拆出来,对 URL 做分组计数并倒序排序,就能看到最常被访问的前 N 个地址。不过,原始日志直接统计往往会有噪声,比如同一个接口带不同查询参数会被拆成多条记录,静态资源和健康检查请求也会占据榜单。本文会从日志格式开始,逐步给出命令行、脚本和工具三类方案,让统计结果更贴近真实业务。

如何统计Nginx日志中访问量最高的Top N个URL?

先理解 Nginx 日志中的 request 字段

Nginx 的默认访问日志格式通常由 log_format 指令定义,常见的 combined 格式如下:

log_format combined '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';

上面这段配置中的 $request 变量会输出类似 GET /api/user?id=123 HTTP/1.1 这样的内容,它由请求方法、URL 和协议版本三部分组成。以空格分隔后,$request 是整个日志行的第 6、7、8 个字段:第 6 个是请求方法,第 7 个是我们真正关心的 URL,第 8 个是协议版本。因此使用 awk 时通常取 $7 就能拿到 URL。

例如一条典型的访问日志如下:

192.168.1.10 - - [10/Mar/2025:10:15:32 +0800] "GET /api/user?id=123 HTTP/1.1" 200 432 "https://ipipp.com" "Mozilla/5.0"

这里第 7 个字段是 /api/user?id=123。如果只是想知道哪些路径被访问最多,还需要把问号后面的查询参数去掉,否则 /api/user?id=123 和 /api/user?id=456 会被当成两个不同的 URL。这个细节在后面的清洗部分会继续展开。

用 awk、sort、uniq 三件套快速统计 Top N

最简单的统计命令只需要一行。先假设日志文件为 access.log,不关心查询参数,只取 URL 的第 7 个字段:

awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -n 10

这条命令的每一步都很明确:awk '{print $7}' 负责提取每行的 URL;sort 把相同 URL 排在一起;uniq -c 统计连续相同行出现的次数;sort -rn 按次数倒序排列;head -n 10 只取前 10 条。实际运行后输出类似这样:

  23847 /api/user
  15012 /api/order
   8730 /api/product
   6544 /login
   3201 /health

但是这种基础统计会包含所有请求,不管状态码是 200、404 还是 500。对于分析正常业务流量,通常应该只统计成功的请求,例如只取状态码为 200 的记录:

awk '$9 == 200 {print $7}' access.log | sort | uniq -c | sort -rn | head -n 20

如果希望把 2xx 和 3xx 都算作正常请求,可以在 awk 中加一个范围判断:

awk '$9 >= 200 && $9 < 400 {print $7}' access.log | sort | uniq -c | sort -rn | head -n 20

这里特别注意 >= 和 && 是 HTML 转义后的写法,源代码中对应的就是大于等于号和逻辑与符号。大日志文件下,sort 会使用磁盘临时文件排序,内存占用相对可控,但命令执行时间会随着日志量线性增长。如果每次统计都要全量扫描,可以考虑先对日志做切割,或者采样最近一段时间的访问记录。

过滤查询参数、静态资源和健康检查

直接统计 URL 最常踩的坑是查询参数导致同一接口分裂。假设客户端请求 /api/user?id=1001 和 /api/user?id=1002,从业务角度看它们应该合并成 /api/user,但直接按原始 URL 统计会出现两个条目,排行就不准确。处理方式是在 awk 中用 split 按问号拆分,只保留问号前面的部分:

awk '{ split($7, arr, "?"); print arr[1] }' access.log | sort | uniq -c | sort -rn | head -n 20

第二类噪声是静态资源。图片、样式表、字体文件通常会被浏览器频繁请求,但它们不反映核心业务接口的访问热度。可以使用正则把常见静态后缀排除掉:

awk '$7 !~ /\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)(\?|$)/ { split($7, arr, "?"); print arr[1] }' access.log | sort | uniq -c | sort -rn | head -n 20

上面的正则中 \. 表示匹配点号,末尾的 (\?|$) 用来兼容带查询参数的静态资源地址。需要注意的是,这条命令先判断原始 URL 是否匹配静态资源,再对非静态资源做查询参数拆分。

第三类需要过滤的是健康检查和内部探活请求。如果服务部署在 Kubernetes 或负载均衡后面,/health、/ping、/ready 这类接口可能每秒被请求很多次,它们会严重挤占 Top N 榜单。可以在 awk 判断条件里直接排除:

awk '$7 !~ /^\/health(\?|$)/ && $7 !~ /^\/ping(\?|$)/ && $7 !~ /^\/ready(\?|$)/ { split($7, arr, "?"); print arr[1] }' access.log | sort | uniq -c | sort -rn | head -n 20

实际使用中,过滤规则最好根据业务场景整理成一个脚本,把状态码、静态资源、健康检查、指定路径都做成配置项,避免每次敲一大串命令。对于较大的日志,可以先压缩保留最近 7 天的文件,再对每一天分别统计,最后合并结果。

用 Python 脚本和工具提升统计效率

命令行方式适合快速查看,但如果过滤规则复杂、需要同时输出多个维度的排行,或者日志格式不是默认的 combined,使用 Python 处理会更清晰。下面是一个可以直接运行的脚本,它会读取 Nginx 日志,提取 URL、去掉查询参数、过滤静态资源和指定状态码,最后输出访问量最高的 10 个 URL:

from collections import Counter
import re

STATIC_SUFFIX = re.compile(r'\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)(\?|$)')
EXCLUDE_PATHS = {'/health', '/ping', '/ready'}

def parse_url(request):
    parts = request.split()
    if len(parts) < 2:
        return None
    url = parts[1].split('?')[0]
    return url

counter = Counter()
with open('/var/log/nginx/access.log', 'r', encoding='utf-8', errors='ignore') as f:
    for line in f:
        parts = line.split()
        if len(parts) < 9:
            continue
        status = parts[8]
        if status not in {'200', '301', '302', '304'}:
            continue
        url = parse_url(' '.join(parts[5:8]))
        if not url or STATIC_SUFFIX.search(url) or url in EXCLUDE_PATHS:
            continue
        counter[url] += 1

for url, count in counter.most_common(10):
    print(f'{count:>6} {url}')

这段代码在逐行处理时先按空格拆分日志,取出状态码和 request 字段,再从 request 中提取 URL。状态码可以根据业务放宽到 2xx 和 3xx,也可以把所有 4xx 排除。Counter 的 most_common 方法内部会自动按次数降序排列,输出结果与 sort -rn 类似。

如果不想写代码,也可以使用现成的日志分析工具。GoAccess 是一个开源的实时日志分析器,支持终端展示和 HTML 报告,能够自动识别 Nginx 日志格式,并直接给出 URL 访问排行、状态码分布、访客来源等指标。ngxtop 则更轻量,可以像 top 命令一样实时查看当前访问最频繁的接口。对于已经接入 ELK、Loki 或 ClickHouse 的团队,更推荐用查询语句完成 Top N 统计,例如在 Kibana 里按 request_path 聚合,这样还可以结合时间筛选和业务字段做多维分析。

无论采用哪种方式,都要先确认日志格式和字段位置是否与假设一致。修改过 log_format 后,awk 的 $7 可能不再指向 URL,脚本中的索引也会变化。建议在统计前先取几行日志做验证,必要时在脚本里通过正则提取 $request 的值,而不是写死第几个字段。这样统计脚本的兼容性会更好。

Nginx日志分析URL访问排行Top N修改时间:2026-09-30 23:09:01

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0930/64001.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。