用awk提取日志耗时字段并数值降序取top10最稳:先确认字段位置,用awk切出数字(避免空格不规律或特殊字符干扰),加sort -k1,1nr和head -10,注意LC_ALL=C、内存限制及gzip支持。

用 awk 提取耗时字段再排序
访问日志里耗时通常在每行末尾或固定列(比如 Nginx 的 $request_time,Apache 的 %D),直接用 awk 切出数字最稳。别信“用 cut -d' ' -fN”,空格不规律时会错位。
常见错误:把日志里 IP 或 URL 里的点、斜杠当分隔符,结果切到一半的耗时值(比如 "0.876" 变成 "0.87")。
- 先确认耗时字段位置:用
head -1 access.log | awk '{print NF; print $0}'看总字段数和样例 - 如果耗时在倒数第 2 列(如 Nginx 默认格式),用
awk '{print $(NF-1), $0}' access.log把耗时提到最前,方便排序 - 加
-F'指定分隔符(比如 Nginx 用空格,但有些日志用双引号包字段,这时得先用sed清洗)
按数值降序取 top 10,不是字典序
sort -k1 默认是字符串排序,"10.2" 会排在 "2.5" 前面——这明显不对。必须加 -n(数值)和 -r(降序)。
性能影响:大日志(GB 级)直接 sort 会吃光内存,sort -S 512M 可限制内存用量;更稳妥是先用 awk 过滤掉明显快的请求(比如 $NF ),再排。
- 完整管道:
awk '{print $(NF-1), $0}' access.log | sort -k1,1nr | head -10 - 如果耗时字段含单位(如
"1.234s"),先用gsub(/[^0-9.]/,"")清洗 - 注意时区或日志轮转导致的字段偏移——同一份日志里可能混着不同格式,建议先
grep -v "^\-" access.log排除注释行
处理 Nginx 的 $upstream_response_time 字段
如果后端有代理(如反向代理到 Flask/Java),真正慢的是 $upstream_response_time,不是 $request_time。这个字段可能是 "-"(超时) 或 "0.001, 0.002"(多段 upstream),直接当数字排会失败。
容易踩的坑:用 awk '$NF != "-" {print $NF}' 忽略 "-" 是对的,但遇到逗号分隔的多值就得拆开。实际慢接口往往对应最大那段 upstream 耗时。
- 提取并取最大值:
awk -F', ' '{max=0; for(i=1;i max) max=$i+0; if(max>0) print max, $0}' access.log - 避免科学计数法干扰:某些日志里超长耗时写成
1.23e-02,awk能直接算,但sort -n也认 - 加
2>/dev/null屏蔽awk对非数字字段的警告(比如空字段)
封装成可复用的 shell 函数
每次改路径、字段号太麻烦,写成函数后,slowlog access.log 12 就能指定第 12 列为耗时字段。
别硬编码 $(NF-1)——不同日志格式列数差异大,传参更灵活;也别用 source 加载一堆函数,单脚本搞定最省心。
- 示例函数:
slowlog() { local log="$1" col="${2:-$(awk '{print NF; exit}' "$log" | xargs echo -n | wc -w)}" awk -v c="$col" '$c ~ /^[0-9.]+([eE][+-]?[0-9]+)?$/ {print $c, $0}' "$log" 2>/dev/null | \ sort -k1,1nr | head -10 | cut -d' ' -f2- } - 调用时加
LC_ALL=C避免 locale 导致sort排序异常(比如某些中文环境把点号当分隔符) - 真实生产环境日志量大,别漏了
zcat支持:zcat access.log.*.gz | slowlog /dev/stdin 12
字段位置和清洗逻辑比排序命令本身更容易出错,尤其混用多种日志格式时,先用小样本验证输出是否含完整原始行,再跑全量。


















