从日志流计算指标与直方图
直接从流式日志数据中汇总请求速率、百分位数和前 N 名报表。
从日志流计算指标与直方图 是 CoddyKit 上的免费 Linux Command Line & Bash Scripting Mastery 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Linux Command Line & Bash Scripting Mastery 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。
为什么要从原始日志计算指标?
生产系统每秒会产生数千行日志。与其将原始日志发送到昂贵的分析平台,不如直接在 shell 中计算请求速率、百分位数和前 N 项报告,成本几乎为零。
- 请求速率:您的服务每秒或每分钟处理多少请求?
- 延迟百分位数:响应时间的 p50、p95、p99 是多少?
- 前 N 项报告:哪些端点、IP 或错误代码出现得最频繁?
awk、sort、uniq 和 bc 等 shell 工具构成了一个强大且可组合的管道,可以从实时日志流或历史文件中回答这些问题,而无需离开终端。
常见访问日志的组成
大多数 Web 服务器都以组合日志格式写入日志。理解其中的字段,是构建所有指标管道的基础:
127.0.0.1 - frank [10/Oct/2024:13:55:36 -0700] "GET /api/users HTTP/1.1" 200 2326 0.042- 字段 1:客户端 IP
- 字段 4(方括号内):时间戳
- 字段 7(带引号):HTTP 方法 + 路径
- 字段 9:状态代码
- 字段 10:响应字节数
- 字段 11:响应时间(秒)(自定义字段,不一定存在)
使用 awk 按位置引用字段($1、$9 等)。只有在进行谨慎分割时,引号内的字段才会被视为一个标记——请使用 awk -F'"' 将日志行按引号分割,或分多次处理。
计算每分钟的请求速率
要计算每分钟请求数,请提取每个时间戳中的分钟部分并统计出现次数。模式 [day/Mon/year:HH:MM 会为您提供小时:分钟这一时间桶。
下面的管道读取 access.log,并输出一张分钟 → 请求数表:
#!/usr/bin/env bash
# Requests per minute from an nginx/apache access log
# Usage: bash req_per_min.sh access.log
LOG="${1:-access.log}"
awk '{
# Extract [day/Mon/year:HH:MM from field 4
match($0, /\[([^:]+:[0-9]+:[0-9]+)/, arr)
minute = arr[1]
if (minute != "") count[minute]++
} END {
for (m in count) print count[m], m
}' "$LOG" | sort -k2使用实时流计算滑动窗口速率
对于实时跟踪,您需要使用滑动窗口。诀窍是将 tail -f 的输出通过管道传给 awk,并使用系统时钟(systime())每 N 秒重置计数器。
此示例每 10 秒输出一行速率:
#!/usr/bin/env bash
# Live request rate — prints lines/10s from a tailed log
# Usage: bash live_rate.sh /var/log/nginx/access.log
LOG="${1:-/var/log/nginx/access.log}"
WINDOW=10
tail -f "$LOG" | awk -v win="$WINDOW" '
BEGIN { start = systime(); count = 0 }
{
count++
now = systime()
if (now - start >= win) {
printf "[%s] %d req/%ds (%.1f req/s)\n",
strftime("%H:%M:%S", now), count, win, count/win
count = 0
start = now
}
}'提取延迟值以计算百分位数
计算百分位数需要对所有观测到的延迟值进行排序。标准步骤如下:
- 将延迟列提取为纯数字列表。
- 按数值排序。
- 使用行数计算,在正确的排名处取值。
下面的脚本提取字段 11(以秒为单位的响应时间),并将其保存到临时文件中,以便在下一步计算百分位数:
#!/usr/bin/env bash
# Extract latency column from access log (field 11)
# Assumes last field on each line is response time in seconds
LOG="${1:-access.log}"
TMP=$(mktemp /tmp/latency_XXXXXX.txt)
awk '{ if ($NF ~ /^[0-9]+\.?[0-9]*$/) print $NF }' "$LOG" \
| sort -n > "$TMP"
echo "Extracted $(wc -l < "$TMP") latency samples -> $TMP"
echo "$TMP" # callers can read this file使用 awk 计算 p50、p95 和 p99
延迟值排序后,选取百分位数就是一个算术问题:第 p 个百分位位于第 ceil(p/100 * N) 行。纯 awk 在将排序后的文件加载到数组后,可以单次遍历完成此操作:
#!/usr/bin/env bash
# Compute p50 / p95 / p99 from a sorted latency file
# Usage: bash percentiles.sh latency_sorted.txt
# Demo: generate 1000 random latencies if no file given
if [[ $# -eq 0 ]]; then
SORTED=$(mktemp)
for i in $(seq 1 1000); do
awk 'BEGIN { srand(); printf "%.4f\n", 0.001 + rand()*0.999 }'
done | sort -n > "$SORTED"
else
SORTED="$1"
fi
awk '
{ values[NR] = $1 }
END {
n = NR
if (n == 0) { print "No data"; exit }
p50 = values[int(n * 0.50 + 0.9999)]
p95 = values[int(n * 0.95 + 0.9999)]
p99 = values[int(n * 0.99 + 0.9999)]
printf "p50 = %.4fs\np95 = %.4fs\np99 = %.4fs\nN = %d\n",
p50, p95, p99, n
}' "$SORTED"构建前 N 个端点报告
前 N 项报告回答的是“哪些路径访问次数最多?”经典的 shell 惯用写法是:
- 使用
awk输出关注的字段(例如 URL 路径) - 使用
sort将相同值分组 - 使用
uniq -c统计连续重复值 - 使用
sort -rn按计数降序排列 - 使用
head -n N取前 N 项
#!/usr/bin/env bash
# Top-10 most requested URL paths from access log
# Usage: bash top_endpoints.sh access.log [N]
LOG="${1:-access.log}"
N="${2:-10}"
echo "=== Top $N endpoints ==="
awk -F'"' '{ print $2 }' "$LOG" \
| awk '{ print $2 }' \
| sort \
| uniq -c \
| sort -rn \
| head -n "$N" \
| awk '{ printf "%6d %s\n", $1, $2 }'按状态代码统计前 N 项:一眼发现错误
HTTP 状态代码可以反映服务的健康状况。按状态代码对日志行分组并计数,可以揭示错误是偶发的还是系统性的。
下面的脚本会生成涵盖所有 2xx、3xx、4xx 和 5xx 桶的明细:
#!/usr/bin/env bash
# Status code frequency breakdown
# Usage: bash status_breakdown.sh access.log
LOG="${1:-access.log}"
echo "=== HTTP Status Code Distribution ==="
awk '{ print $9 }' "$LOG" \
| grep -E '^[0-9]{3}$' \
| sort \
| uniq -c \
| sort -rn \
| awk '{ printf "%6d %s\n", $1, $2 }'
echo
echo "=== 5xx Error Spike Check ==="
awk '$9 ~ /^5/ { print $9, $7 }' "$LOG" \
| sort | uniq -c | sort -rn | head -20使用 awk 创建 ASCII 直方图
文本直方图可以让您在终端或 CI 日志中立即看清延迟分布。步骤如下:
- 将每个延迟值放入一个区间(例如 0-50ms、50-100ms、……)。
- 统计每个区间中的观测值数量。
- 根据最大计数进行缩放,打印由
#字符组成的条形图。
无需图表工具,就能发现双峰分布或异常值,这种方法非常实用。
#!/usr/bin/env bash
# ASCII latency histogram from a list of latency values in seconds
# Demo: generates synthetic data if no input file is provided
if [[ $# -eq 0 ]]; then
# Generate 500 synthetic latencies (ms converted to s)
python3 -c "
import random, math
for _ in range(500):
# bimodal: fast cluster ~50ms, slow cluster ~300ms
if random.random() < 0.75:
v = max(1, random.gauss(50, 15))
else:
v = max(1, random.gauss(300, 80))
print(f'{v/1000:.4f}')
" | sort -n | awk '
{ values[NR] = $1 * 1000 } # convert to ms
END {
buckets = 10; maxVal = 500
step = maxVal / buckets
for (i = 0; i < buckets; i++) hist[i] = 0
for (i = 1; i <= NR; i++) {
b = int(values[i] / step)
if (b >= buckets) b = buckets - 1
hist[b]++
}
maxCount = 0
for (i = 0; i < buckets; i++) if (hist[i] > maxCount) maxCount = hist[i]
print "Latency (ms) Count Distribution"
print "---------------------------------------"
for (i = 0; i < buckets; i++) {
lo = i * step; hi = lo + step
barLen = int(hist[i] / maxCount * 40)
bar = ""
for (j = 0; j < barLen; j++) bar = bar "#"
printf "%4d-%4dms %5d %s\n", lo, hi, hist[i], bar
}
}'
else
echo "Usage: pipe a sorted latency file (in seconds) to this pattern"
fi组合多个指标:一次性摘要报告
生产运行手册通常需要一个命令一次性输出所有关键指标:速率、延迟百分位数、热门端点和错误率。您可以将目前学到的内容组合成一个脚本,供人或告警系统调用。
#!/usr/bin/env bash
# One-shot log summary report
# Usage: bash log_summary.sh access.log
LOG="${1:-access.log}"
[[ -f "$LOG" ]] || { echo "File not found: $LOG"; exit 1; }
TOTAL=$(wc -l < "$LOG")
ERRORS=$(awk '$9 ~ /^[45]/' "$LOG" | wc -l)
ERR_RATE=$(awk "BEGIN { printf \"%.1f\", ($ERRORS / ($TOTAL || 1)) * 100 }")
echo "====================================="
echo " Log Summary: $LOG"
echo "====================================="
printf " Total requests : %d\n" "$TOTAL"
printf " 4xx/5xx errors : %d (%.1f%%)\n" "$ERRORS" "$ERR_RATE"
echo
echo "--- Top 5 Endpoints ---"
awk -F'"' '{ print $2 }' "$LOG" | awk '{ print $2 }' \
| sort | uniq -c | sort -rn | head -5 \
| awk '{ printf " %6d %s\n", $1, $2 }'
echo
echo "--- Latency Percentiles ---"
awk '{ if ($NF ~ /^[0-9]+\.?[0-9]*$/) print $NF * 1000 }' "$LOG" \
| sort -n \
| awk '
{ v[NR]=$1 }
END {
if (NR==0) { print " No latency data"; exit }
printf " p50 = %.1fms\n", v[int(NR*0.50+0.9999)]
printf " p95 = %.1fms\n", v[int(NR*0.95+0.9999)]
printf " p99 = %.1fms\n", v[int(NR*0.99+0.9999)]
}'通过命名管道和 tee 流式传输指标
在可观测性管道中,您经常需要分流日志流:将原始行写入磁盘,同时计算指标。使用命名管道(mkfifo)配合 tee,无需将整个流缓存在内存中即可实现这一点。
mkfifo /tmp/log_pipe— 创建命名管道tee /tmp/log_pipe | metric_consumer &— 分出一个分支交给指标消费者- 另一个分支将数据写入归档文件
这种模式将磁盘写入与指标聚合解耦,并允许任一侧独立重启。
#!/usr/bin/env bash
# Fan-out: write to archive AND count errors in real time
# Run: bash fanout_pipeline.sh /var/log/nginx/access.log
LOG="${1:-/var/log/nginx/access.log}"
ARCHIVE="/tmp/access_archive.log"
PIPE="/tmp/log_metrics_pipe"
# Clean up on exit
trap 'rm -f "$PIPE"' EXIT
mkfifo "$PIPE"
# Branch 1: count 5xx errors per minute from the pipe
awk '$9 ~ /^5/ {
match($0, /\[([^:]+:[0-9]+:[0-9]+)/, arr)
errors[arr[1]]++
} END {
for (m in errors) printf "5xx errors at %s: %d\n", m, errors[m]
}' "$PIPE" &
# Branch 2: archive to disk + feed Branch 1 via pipe
tail -f "$LOG" | tee "$PIPE" >> "$ARCHIVE"哪种 awk 技巧可以正确计算已排序的 N 个延迟值数组的 p95 百分位数?
您已将 N 个已排序的延迟值加载到 awk 数组 v[1..N] 中。哪个表达式可以正确取得 p95 百分位数?
课程回顾:日志流中的指标与直方图
在本课程中,您直接使用 Bash 构建了一套完整的指标流水线:
- 请求速率:
awk从时间戳中提取分钟桶并统计出现次数;tail -f与systime()结合后可以得到实时滑动窗口速率。 - 延迟百分位数:提取延迟列,使用
sort -n排序,然后在 awk 中使用int(N * p + 0.9999)选取ceil(p/100 * N)所对应的行。 - 前 N 项报告:经典的
awk | sort | uniq -c | sort -rn | head -N流水线适用于任何分类字段(路径、IP、状态码)。 - ASCII 直方图:将值分桶,统计每个区间中的数量,并根据最大分桶数量缩放条形长度,从而一眼查看分布情况。
- 使用命名管道进行扇出:
mkfifo与tee结合后,可以同时归档原始日志并将数据提供给指标使用者,而无需将整个流加载到内存中。
这些可组合的基础组件消除了故障期间对外部工具的需求,并构成了轻量级可观测性脚本的基础;只要能运行 Bash,这些脚本就能在任何地方运行。
常见问题解答
「从日志流计算指标与直方图」课时是免费的吗?
是的 — 「从日志流计算指标与直方图」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Linux Command Line & Bash Scripting Mastery 课程的其余内容,请升级到 CoddyKit PRO。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。
「从日志流计算指标与直方图」这节课中我会学到什么?
直接从流式日志数据中汇总请求速率、百分位数和前 N 名报表。 你通过在浏览器中直接运行的动手代码来练习 Linux Command Line & Bash Scripting Mastery,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Linux Command Line & Bash Scripting Mastery 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Linux Command Line & Bash Scripting Mastery 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「从日志流计算指标与直方图」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Linux Command Line & Bash Scripting Mastery 课中编写并运行代码吗?
能。每节 Linux Command Line & Bash Scripting Mastery 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。