最新 Linux awk 命令实战教程:从日志分析到性能监控

大家好,我是小康。上次我们一起学习了 Linux 的 sed 命令,今天要介绍的是文本处理的"瑞士军刀" —— awk。无论是分析日志、处理数据,还是提取信息,它都能帮你轻松搞定。

第一部分: 初识 awk

作为一名开发老兵,我整理了这些年和 awk 打交道的心得。希望能帮你少走弯路,快速掌握这个强大的工具。

记得刚入行那会儿,面对成堆的日志文件,我跟大多数新手一样一筹莫展。直到遇到了 awk 这个老伙计,才算找到了"趁手的兵器"。

今天,就让我用一个开发工程师的视角,带你认识这个陪伴了我 6 年多的老朋友。

1. 第一次相遇:awk 是个什么样的角色?

就像一个心灵手巧的老师傅,awk 最擅长的就是把大段大段的文本"解剖"开来,精准地找出你想要的信息。它的名字来自三位创始人(Aho、Weinberger、Kernighan)的首字母,虽然不好念,但本事真不小。

2. 从一个真实案例开始

还记得我遇到的第一个挑战:leader 让我从一个几GB的服务日志里找出造成系统故障的元凶。

当时的日志大概长这样:

复制
2024-02-13 10:00:01 [192.168.1.100] "GET /api/users" 200 89ms 2024-02-13 10:00:02 [192.168.1.101] "POST /api/orders" 500 1230ms 2024-02-13 10:00:03 [192.168.1.102] "GET /api/products" 200 45ms1.2.3.

我需要:

找出所有响应时间超过1秒的请求分析高峰期的访问量......

用 awk 的解决方案出奇简单:

复制
# 1、找出所有响应时间超过1秒的请求 awk { # 提取并转换响应时间 time = $7 # 取最后一个字段 gsub(/ms/, "", time) # 去掉ms time = time + 0 # 确保转成数字 # 只打印超过1秒(1000ms)的请求 if(time >= 1000) { printf "时间: %s %s\nIP: %s\n请求: %s %s\n响应时间: %dms\n----------\n", $1, $2, substr($3, 2, length($3)-2), $4, $5, time } } access.log # 输出: 时间: 2024-02-13 10:00:02 IP: 192.168.1.101 请求: "POST /api/orders" 响应时间: 1230ms ---------- # 2、分析高峰期的访问量 awk BEGIN { print "每分钟访问量统计:" print "-------------------" } { # 提取时分 split($2, t, ":") minute = t[1] ":" t[2] # 只取小时和分钟 count[minute]++ } END { # 按时间排序输出 n = asorti(count, sorted) for(i=1; i<=n; i++) { printf "%s:00 - %d次访问\n", sorted[i], count[sorted[i]] } } access.log # 输出: 每分钟访问量统计: ------------------- 10:00:00 - 3次访问 10:01:00 - 2次访问 10:02:00 - 1次访问1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.

第二部分 : awk 基本功

老规矩,我们先来看看最常用的 awk 基础命令。这些都是我这些年解决问题的"杀手锏",保证你学了就能用。

1. awk的基本结构

在开始学习具体命令前,我们先来了解awk程序的基本结构:

复制
awk BEGIN {动作前} pattern {动作} END {动作后} 文件名1.2.3.

就像一个完整的故事有开头、主体和结尾,awk 也有三个主要部分:

(1) BEGIN块:开场白

在读取文件前执行常用来打印表头、初始化变量
复制
# 例如:输出前先打印个表头 BEGIN {print "=== 进程列表 ==="}1.2.

(2) pattern {action}:主体部分

pattern:匹配条件,决定要处理哪些行action:具体操作,决定要做什么
复制
# 例如:找出root的进程 $1=="root" {print $0}1.2.

(3) END块:收尾工作

在处理完所有行后执行常用来输出统计结果
复制
# 例如:最后输出总行数 END {print "共有"NR"个进程"}1.2.

此外,awk 还提供了一些常用的内置变量:

$0:整行内容2..:第1、2列NR:当前行号NF:当前行的列数2. 实例讲解

理解了基本结构,我们来看些实际例子。假设我们有一个进程列表 process.txt:

复制
root 1234 5.0 2.5 mysql running admin 2345 3.2 1.5 nginx running root 3456 8.5 4.0 java stopped nobody 4567 2.1 1.0 nginx running1.2.3.4.

(1) 提取特定列

复制
# 看看谁在运行这些进程 awk {print $1} process.txt # 输出: root admin root nobody # 查看进程名和状态 awk {print $5, $6} process.txt # 输出: mysql running nginx running java stopped nginx running1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.

(2) 条件过滤(最常用)

复制
# 找出 CPU 使用率超过5%的进程 awk $3 > 5 {print $5 "进程CPU使用率:", $3"%"} process.txt # 输出: java进程CPU使用率: 8.5% # 找出状态为 running 的进程 awk $6=="running" {print $1,$5} process.txt # 输出: root mysql admin nginx nobody nginx1.2.3.4.5.6.7.8.9.10.11.12.13.
2. 实用统计功能

(1) 常用统计

复制
# 统计进程数量, NR: NR 是 awk 的一个内置变量,表示当前已经处理的记录(行)数量。 awk END {print "总进程数:", NR} process.txt # 输出: 总进程数: 4 # 我们也可以在处理过程中看到NR的变化 awk {print "当前处理第" NR "行"} process.txt # 输出: 当前处理第1行 当前处理第2行 当前处理第3行 当前处理第4行 # 计算所有进程的平均CPU使用率 awk {sum += $3} END {print "平均CPU使用率:", sum/NR"%"} process.txt # 输出: 平均CPU使用率: 4.7%1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.

(2) 分组统计(特别常用)

复制
# 看看每个用户开了多少个进程 awk {count[$1]++} END { for(user in count) { print user "的进程数:", count[user] } } process.txt # 输出: root的进程数: 2 admin的进程数: 1 nobody的进程数: 1 # 统计每种状态的进程数 awk {states[$6]++} END { for(state in states) { print state, states[state] } } process.txt # 输出: running 3 stopped 11.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.
3. 实战常用技巧

(1) 匹配特定内容

复制
# 找出 java 相关的进程 awk /java/ {print $0} process.txt # $0 代表当前行的整行内容 # 输出: root 3456 8.5 4.0 java stopped # 找出包含特定字符的行并突出显示重要信息 awk /nginx/ {print "进程ID:"$2, "内存:"$4"%"} process.txt # 输出: 进程ID:2345 内存:1.5% 进程ID:4567 内存:1.0%1.2.3.4.5.6.7.8.9.10.11.12.

(2) 多条件组合(经常用到)

复制
# 找出 CPU 高、状态为 running 的进程 awk $3 > 3 && $6=="running" { print "警告 -", $5, "进程CPU使用率:", $3"%" } process.txt # 输出: 警告 - mysql 进程CPU使用率: 5.0% 警告 - nginx 进程CPU使用率: 3.2%1.2.3.4.5.6.7.8.
4. 小贴士

(1) 实用的判断方法:

复制
# 找出异常的进程(CPU或内存使用过高) awk $3 > 5 || $4 > 3 { print $5 "进程异常:" print " CPU:", $3"%" print " 内存:", $4"%" } process.txt # 输出: java进程异常: CPU: 8.5% 内存: 4.0%1.2.3.4.5.6.7.8.9.10.11.

(2) 累加统计:

复制
bash # 计算 nginx 进程的总内存占用 awk /nginx/ {total += $4} END {print "nginx总内存占用:", total"%"} process.txt # 输出: nginx总内存占用: 2.5%1.2.3.4.5.6.7.

记住:

$1,$2,$3... 代表第几列NR 代表当前行号print 和 printf 都是打印命令用 $0 可以打印整行

这些都是我平时工作中最常用的简单命令,基本够用了。等你熟悉了这些,我们再学更高级的用法。

第三部分: awk高级应用指南(性能分析)

接下来我们来点高级的,带大家用 awk 处理日常工作中最常见的几个场景。每一步我们都从简单的开始,循序渐进地掌握。

1. 基础日志处理

先从一个简单的接口日志开始:

复制
2024-02-14 10:00:01 [api=/user/login] cost=100ms status=200 2024-02-14 10:00:02 [api=/user/info] cost=50ms status=200 2024-02-14 10:00:03 [api=/user/login] cost=800ms status=500 2024-02-14 10:00:04 [api=/order/create] cost=150ms status=2001.2.3.4.

(1) 提取重要信息(简单)

复制
# 只看接口名和响应时间 awk {print $3, $4} api.log # 输出: [api=/user/login] cost=100ms [api=/user/info] cost=50ms [api=/user/login] cost=800ms [api=/order/create] cost=150ms1.2.3.4.5.6.7.8.

(2) 查找异常请求(常用)

复制
# 找出响应时间超过500ms的慢请求 awk { # 提取响应时间的数字部分 gsub(/cost=|ms/, "", $4) # 去掉"cost=""ms" # 如果响应时间超过500ms if($4 > 500) { print "慢请求: " $0 } } api.log # 输出: 慢请求: 2024-02-14 10:00:03 [api=/user/login] cost=800ms status=5001.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
2. 接口性能分析

(1) 计算接口的平均响应时间(入门级)

复制
# 计算每个接口的平均响应时间 awk { # 提取接口名称 api=$3 # 提取响应时间的数字部分 gsub(/.*=|ms.*/, "", $4) # 累加响应时间 sum[api] += $4 # 统计请求次数 count[api]++ } END { print "接口平均响应时间:" for(api in sum) { printf "%s: %.2fms\n", api, sum[api]/count[api] } } api.log # 输出: 接口平均响应时间: [api=/user/login]: 450.00ms [api=/user/info]: 50.00ms [api=/order/create]: 150.00ms1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

(2) 统计接口QPS(常用)

先从一个简单的接口日志开始:

复制
2024-02-14 10:00:01 [api=/user/login] cost=100ms status=200 2024-02-14 10:00:02 [api=/user/info] cost=50ms status=200 2024-02-14 10:00:03 [api=/user/login] cost=800ms status=500 2024-02-14 10:00:04 [api=/order/create] cost=150ms status=2001.2.3.4.
复制
# 命令:计算每秒的请求数(QPS) awk { # 把时间列拼接起来: $1是日期,$2是时间 # 例如: "2024-02-14 10:00:01" time = $1" "$2 # substr 函数用于截取字符串 # 从拼接的时间字符串中取前19位,精确到秒 # 如: "2024-02-14 10:00:01" second = substr(time, 1, 19) # 用时间作为key,计数+1 count[second]++ } END { # 处理完所有行后,打印统计结果 print "每秒请求数(QPS):" # 遍历统计结果 for(s in count) { print s ": " count[s] "次/秒" } } api.log1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.

(3) 分析响应时间分布(进阶)

复制
# 按区间统计响应时间分布 awk BEGIN { print "响应时间分布统计:" } { # 提取cost=后面的数字,去掉ms split($4, arr, "=|ms") # 用=或ms分割,如:"cost=100ms" -> arr[2]="100" time = arr[2] # 提取数字部分 # 按区间统计请求数 if(time <= 100) { range["0-100ms"]++ # 统计小于等于100ms的请求 } else if(time <= 200) { range["101-200ms"]++ # 统计101ms到200ms的请求 } else { range["200ms+"]++ # 统计大于200ms的请求 } total++ # 总请求数加1 } END { # 遍历每个区间并打印统计结果 for(r in range) { percent = range[r]/total*100 printf "%s: %d个请求 (%.1f%%)\n", r, range[r], percent } } api.log # 现在输出应该是: 响应时间分布统计: 0-100ms: 2个请求 (50.0%) 101-200ms: 1个请求 (25.0%) 200ms+: 1个请求 (25.0%)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.
3. 错误分析

统计错误率(常用)

复制
2024-02-14 10:00:01 [api=/user/login] cost=100ms status=200 2024-02-14 10:00:02 [api=/user/info] cost=50ms status=200 2024-02-14 10:00:03 [api=/user/login] cost=800ms status=500 2024-02-14 10:00:04 [api=/order/create] cost=150ms status=200 # 计算接口错误率 awk { api=$3 status=$5 gsub(/.*=/, "", status) # 统计总请求和错误请求 total[api]++ if(status >= 400) { errors[api]++ } } END { print "接口错误率统计:" for(api in total) { if(errors[api] > 0) { err_rate = errors[api]/total[api]*100 printf "%s: %.1f%% (%d/%d)\n", api, err_rate, errors[api], total[api] } } } api.log # 输出: 接口错误率统计: [api=/user/login]: 50.0% (1/2)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.
4. 生成性能报告(高级)

把前面学到的都用上,生成一个完整的性能报告:

复制
# 生成完整的接口性能分析报告 awk BEGIN { print "=== 接口性能分析报告 ===" print "时间范围:" strftime("%Y-%m-%d %H:%M:%S") print "\n1. 总体统计" } { # 记录基础信息 api=$3 gsub(/.*=|ms.*/, "", $4) cost=$4 gsub(/.*=/, "", $5) status=$5 # 统计总请求 total_reqs++ # 按接口统计 reqs[api]++ total_cost[api] += cost # 记录最大最小响应时间 if(cost > max_cost[api]) max_cost[api] = cost if(min_cost[api] == 0 || cost < min_cost[api]) min_cost[api] = cost # 统计错误 if(status >= 400) errors[api]++ } END { # 1. 打印总体统计 printf "总请求数:%d\n", total_reqs # 2. 打印接口详情 print "\n2. 接口详情" for(api in reqs) { printf "\n接口:%s\n", api printf " 总调用次数:%d\n", reqs[api] printf " 平均响应时间:%.2fms\n", total_cost[api]/reqs[api] printf " 最大响应时间:%dms\n", max_cost[api] printf " 最小响应时间:%dms\n", min_cost[api] if(errors[api] > 0) { printf " 错误率:%.1f%%\n", errors[api]/reqs[api]*100 } } } api.log # 输出: === 接口性能分析报告 === 时间范围:2024-02-14 10:00:00 1. 总体统计 总请求数:4 2. 接口详情 接口:[api=/user/login] 总调用次数:2 平均响应时间:450.00ms 最大响应时间:800ms 最小响应时间:100ms 错误率:50.0% 接口:[api=/user/info] 总调用次数:1 平均响应时间:50.00ms 最大响应时间:50ms 最小响应时间:50ms 接口:[api=/order/create] 总调用次数:1 平均响应时间:150.00ms 最大响应时间:150ms 最小响应时间:150ms1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.
5. 实用小技巧

(1) 处理大文件时先取样分析:

复制
head -1000 big_log.txt | awk 你的命令1.

(2) 实时监控错误和慢请求:

复制
测试用例: ❯ cat api.log # api.log 示例数据: 2024-02-14 10:00:01 [api=/user/login] cost=100ms status=200 # 正常请求 2024-02-14 10:00:02 [api=/user/info] cost=550ms status=200 # 慢请求(>500ms) 2024-02-14 10:00:03 [api=/user/login] cost=800ms status=500 # 慢请求且报错 2024-02-14 10:00:04 [api=/order/create] cost=150ms status=404 # 错误请求 2024-02-14 10:00:05 [api=/user/profile] cost=200ms status=200 # 正常请求 # 监控命令: tail -f api.log | awk $4 ~ /cost=[5-9][0-9][0-9]ms/ || $5 ~ /status=[45][0-9][0-9]/ { # 检查是否是慢请求 if($4 ~ /cost=[5-9][0-9][0-9]ms/) { msg="慢请求" } # 检查是否有错误状态码 if($5 ~ /status=[45][0-9][0-9]/) { msg=msg?msg" 且 状态码异常":"状态码异常" } # 打印告警信息 print "\033[31m告警:" $0 " # " msg "\033[0m" # 重置消息变量 msg="" } # 输出(红色显示): 告警:2024-02-14 10:00:02 [api=/user/info] cost=550ms status=200 # 因为响应时间>500ms 告警:2024-02-14 10:00:03 [api=/user/login] cost=800ms status=500 # 因为响应时间>500ms且状态码500 告警:2024-02-14 10:00:04 [api=/order/create] cost=150ms status=404 # 因为状态码4041.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.

记住:

先从简单的统计开始需要时再加更多的统计维度复杂的分析可以分步骤进行多用print调试你的统计逻辑

学会了这些,你就能应对大部分的日志分析工作了!

第四部分:实战篇 - 应用日志分析

接着我们来分析实际工作中最常见的几种应用日志。咱们由浅入深,一步步来。

1. 基础日志分析

(1) 简单的应用日志

先来看一个最基础的应用日志:

复制
2024-02-14 10:00:01 [INFO] UserService - 用户登录成功,用户名=admin 2024-02-14 10:00:02 [ERROR] OrderService - 订单创建失败:数据库连接超时 2024-02-14 10:00:03 [WARN] UserService - 密码错误,用户名=test 2024-02-14 10:00:04 [ERROR] PaymentService - 支付失败:余额不足1.2.3.4.

(2) 基础日志过滤(最简单的用法)

复制
# 命令1:显示所有ERROR日志 awk /ERROR/ app.log # 输出: 2024-02-14 10:00:02 [ERROR] OrderService - 订单创建失败:数据库连接超时 2024-02-14 10:00:04 [ERROR] PaymentService - 支付失败:余额不足 # 命令2:查看特定服务的日志 awk /UserService/ app.log # 输出: 2024-02-14 10:00:01 [INFO] UserService - 用户登录成功,用户名=admin 2024-02-14 10:00:03 [WARN] UserService - 密码错误,用户名=test1.2.3.4.5.6.7.8.9.10.11.12.13.

(3) 统计日志级别(常用功能)

复制
# 命令:统计每种日志级别的数量 awk # 匹配有方括号的行 /\[.*\]/ { # 提取方括号中的内容,存入arr数组 match($0, /\[(.*?)\]/, arr) # 对应的日志级别计数加1 level[arr[1]]++ } # 所有行处理完后执行 END { print "日志级别统计:" # 遍历统计结果并打印 for(l in level) { print l ": " level[l] "条" } } app.log # 输出: 日志级别统计: INFO: 1ERROR: 2WARN: 1条1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.
2. 接口调用日志分析

(1) 接口日志示例

复制
2024-02-14 10:00:01 [api=/user/login] cost=120ms status=200 2024-02-14 10:00:02 [api=/order/create] cost=500ms status=500 2024-02-14 10:00:03 [api=/user/info] cost=80ms status=2001.2.3.

(2) 分析接口响应时间

复制
# 命令:统计每个接口的平均响应时间 awk { # 提取接口名和响应时间 api=$3 # 获取接口名称列 gsub(/\[|\]/, "", api) # 去掉方括号 gsub(/.*=|ms/, "", $4) # 提取响应时间的数字部分 # 统计数据 apis[api] += $4 # 累加响应时间 count[api]++ # 统计调用次数 } END { print "接口平均响应时间:" for(a in apis) { printf "%s: %.2fms\n", a, apis[a]/count[a] } } api.log # 输出: 接口平均响应时间: api=/user/login: 120.00ms api=/order/create: 500.00ms api=/user/info: 80.00ms1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.
3. 错误日志分析

(1) 异常堆栈日志

复制
> cat Service.log 2024-02-14 10:00:01 [ERROR] NullPointerException: 空指针异常 at com.example.UserService.getUser(UserService.java:15) at com.example.UserController.login(UserController.java:10) 2024-02-14 10:00:02 [ERROR] SQLException: 数据库连接失败 at com.example.OrderService.create(OrderService.java:25)1.2.3.4.5.6.

(2) 提取完整异常信息

复制
# 命令:提取异常信息及其堆栈 awk # 匹配错误行 /ERROR/ { print "\n发现异常:" print $0 # 打印错误行 print "异常堆栈:" } # 匹配堆栈信息(以空格开头的行) /^[[:space:]]/ { print $0 # 打印堆栈行 } Service.log # 输出: 发现异常: 2024-02-14 10:00:01 [ERROR] NullPointerException: 空指针异常 异常堆栈: at com.example.UserService.getUser(UserService.java:15) at com.example.UserController.login(UserController.java:10) 发现异常: 2024-02-14 10:00:02 [ERROR] SQLException: 数据库连接失败 异常堆栈: at com.example.OrderService.create(OrderService.java:25)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.
4. 性能问题分析

(1) 数据库慢查询日志

复制
2024-02-14 10:00:01 [SLOW_QUERY] cost=2.5s sql="SELECT * FROM orders WHERE user_id=123" 2024-02-14 10:00:05 [SLOW_QUERY] cost=1.8s sql="UPDATE users SET status=1" 2024-02-14 10:00:10 [SLOW_QUERY] cost=3.1s sql="SELECT * FROM order_items"1.2.3.

(2) 分析慢查询

复制
# 命令:分析超过2秒的慢查询 awk { # 提取执行时间,去掉s得到纯数字 time_str = $4 gsub("cost=|s", "", time_str) # 将cost=和s都替换为空 time = time_str + 0 # 转换为数字 # 提取完整SQL语句 sql = substr($0, index($0, "sql=")) # 如果查询时间超过2if(time > 2) { printf "\n时间:%s %s\n", $1, $2 printf "耗时:%.1f秒\n", time printf "SQL:%s\n", sql printf "----------\n" } } slow_query.log # 输出: 时间:2024-02-14 10:00:01 耗时:2.5SQL"SELECT * FROM orders WHERE user_id=123" ---------- 时间:2024-02-14 10:00:10 耗时:3.1SQL"SELECT * FROM order_items" ----------1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.
5. 监控告警分析

(1) 告警日志

复制
2024-02-14 10:00:01 [ALERT] service=order-service type=cpu_high value=92% 2024-02-14 10:00:05 [ALERT] service=user-service type=memory_high value=85% 2024-02-14 10:00:10 [ALERT] service=order-service type=disk_usage value=95%1.2.3.

(2) 统计告警情况

复制
# 命令:按服务统计告警 awk BEGIN { print "=== 告警分析报告 ===" print "分析时间:" strftime("%Y-%m-%d %H:%M:%S") print "-------------------" } /\[ALERT\]/ { # 只处理包含[ALERT]的行 # 提取基本信息 gsub(/service=|type=|value=|%|threshold=/, " ", $0) for(i=1; i<=NF; i++) { if($i == "[ALERT]") { service = $(i+1) # 服务名 type = $(i+2) # 告警类型 value = $(i+3) # 当前值 threshold = $(i+4) # 阈值 } } # 计算超出阈值的百分比 exceed = value - threshold # 根据超出程度分级 if(exceed >= 20) { level = "严重" } else if(exceed >= 10) { level = "警告" } else { level = "注意" } # 统计信息 services[service]++ types[type]++ levels[level]++ # 记录最大值和时间 if(max_value[type] < value) { max_value[type] = value max_time[type] = $1 " " $2 } # 保存详细信息 details[++count] = sprintf("时间:%s %s\n服务:%-15s 类型:%-12s 当前值:%d%% (超出阈值:%d%%) 级别:%s", $1, $2, service, type, value, exceed, level) } END { # 1. 告警级别统计 print "\n1. 告警级别分布:" for(l in levels) { printf "%-6s: %d次\n", l, levels[l] } # 2. 服务告警统计 print "\n2. 服务告警统计:" for(svc in services) { printf "%-20s: %d次告警\n", svc, services[svc] } # 3. 告警类型统计 print "\n3. 告警类型统计:" for(t in types) { printf "%-15s: %d次\n", t, types[t] printf " 最大值: %d%% (发生时间: %s)\n", max_value[t], max_time[t] } # 4. 详细告警记录 print "\n4. 详细告警记录:" print "-------------------" for(i=1; i<=count; i++) { # 使用count而不是NR print details[i] "\n----------" } } alert.log # 输出: 告警统计: === 告警分析报告 === 分析时间:2025-02-14 21:34:52 ------------------- 1. 告警级别分布: 注意 : 3警告 : 22. 服务告警统计: order-service : 3次告警 user-service : 2次告警 3. 告警类型统计: memory_high : 2最大值: 95% (发生时间: 2024-02-14 10:00:20) cpu_high : 2最大值: 92% (发生时间: 2024-02-14 10:00:01) disk_usage : 1最大值: 95% (发生时间: 2024-02-14 10:00:10) 4. 详细告警记录: ------------------- 时间:2024-02-14 10:00:01 服务:order-service 类型:cpu_high 当前值:92% (超出阈值:12%) 级别:警告 ---------- 时间:2024-02-14 10:00:05 服务:user-service 类型:memory_high 当前值:85% (超出阈值:5%) 级别:注意 ---------- 时间:2024-02-14 10:00:10 服务:order-service 类型:disk_usage 当前值:95% (超出阈值:5%) 级别:注意 ---------- 时间:2024-02-14 10:00:15 服务:user-service 类型:cpu_high 当前值:88% (超出阈值:8%) 级别:注意 ---------- 时间:2024-02-14 10:00:20 服务:order-service 类型:memory_high 当前值:95% (超出阈值:15%) 级别:警告 ----------1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.

这些是日常工作中最常用到的日志分析场景。我们从最简单的日志过滤开始,逐步深入到了复杂的统计分析。记住,解决复杂的问题时,可以先拆分成小步骤,一步一步来处理。

总结

看到这里,相信你已经掌握了 awk 这个文本处理利器的基本使用。从最初的字段提取,到复杂的日志分析,再到性能监控,只要灵活运用,awk 几乎能解决所有的文本处理需求。

不过,真实的工作环境中,往往需要 多个命令配合使用 才能达到最好的效果。就像武侠小说里的武功招式,单招玩得再熟,也不如组合技来得实用。

比如:

复制
# 先用grep找出错误日志,再用awk分析 grep "ERROR" app.log | awk {print $1,$2} # 用sed处理格式,再用awk统计 sed s/"//g access.log | awk {count[$1]++} END{for(ip in count) print ip,count[ip]}1.2.3.4.5.

下一篇,我将为大家带来 grep、sed、awk 这三剑客的组合应用,教你如何在实战中发挥它们的最大威力。相信这些实用的"组合技",一定能帮你在日常工作中事半功倍。

THE END
本站服务器由亿华云赞助提供-企业级高防云服务器