news 2026/7/5 22:21:14

openEuler下Nginx日志分析:Shell脚本实现轻量级监控与性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
openEuler下Nginx日志分析:Shell脚本实现轻量级监控与性能优化

1. 项目概述与核心价值

最近在折腾一个跑在 openEuler 虚拟机上的 Web 服务,Nginx 的访问日志一天下来能有好几百兆。看着那一行行密密麻麻的日志,想快速知道今天谁访问最频繁、哪个接口响应最慢、有没有异常的爬虫在扫站,靠肉眼一条条看肯定是不现实的。网上虽然有成套的 ELK(Elasticsearch, Logstash, Kibana)或者 Grafana + Loki 方案,但对于单个服务、轻量级的需求来说,部署和维护成本有点高,杀鸡用牛刀了。于是,我就琢磨着写一个轻量级的 Nginx 日志分析脚本,直接在 openEuler 服务器上跑,定时执行,把关键指标以最直观的方式输出,甚至能发个邮件报告。这活儿听起来简单,但真要把日志解析、数据聚合、结果呈现都做好,里面有不少门道。今天就把我折腾这个脚本的过程、踩过的坑以及最终成型的方案,详细跟大家分享一下。无论你是运维新手,还是想给自己负责的服务加个轻量级监控,这套思路和脚本都能直接拿去用。

这个脚本的核心价值在于“轻量”和“直达痛点”。它不依赖复杂的分布式架构,就是纯 Shell 脚本(配合 awk, sort, uniq 这些 Linux 老伙计),加上一点 Python 或 Go 来增强表现力(可选)。你只需要有一台安装了 openEuler(或其他 Linux 发行版)的服务器,Nginx 日志按标准格式输出,就能立刻获得对服务访问情况的洞察力。特别适合个人项目、初创公司早期、或者作为大型监控系统的补充和快速验证工具。

2. 环境准备与日志格式确认

2.1 openEuler 虚拟机基础环境

我的实验环境是一台在 VMware Workstation 17 上安装的 openEuler 22.03 LTS 虚拟机。选择 openEuler 是因为它作为企业级的 Linux 发行版,在安全性、稳定性以及对国产硬件平台的兼容性上表现不错,而且其软件源和社区生态也越来越丰富。对于这个项目来说,任何主流的 Linux 发行版(如 CentOS, Ubuntu, Debian)其实都可以,操作大同小异。

首先,确保你的 openEuler 系统已经安装了必要的工具。打开终端,用 root 或具有 sudo 权限的用户执行:

# 更新系统软件包索引 dnf update -y # 安装本项目可能用到的核心工具 # vim: 文本编辑器,用于编写和修改脚本。 # curl/wget: 用于从网络下载文件(如下载示例日志或安装脚本)。 # awk/sed/grep: 文本处理三剑客,日志分析的基石。 # sort/uniq: 数据排序和去重,用于统计排名。 # mailx: 用于发送邮件报告(如果需要邮件功能)。 dnf install -y vim wget curl gawk sed grep coreutils mailx

如果你的 openEuler 是最小化安装,可能默认没有mailx,需要手动安装mailx或配置其他邮件发送方式(如 Python 的 smtplib)。这里我们先确保基础文本处理工具到位。

2.2 Nginx 日志格式解析

脚本要正确工作,前提是能正确解析 Nginx 的日志格式。Nginx 默认的访问日志格式定义在nginx.conf文件中,通常是log_format指令。最常见的格式是combined

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

对应的一条日志可能长这样:192.168.1.100 - - [10/Apr/2023:15:30:22 +0800] "GET /api/user?id=123 HTTP/1.1" 200 1534 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

我们需要清楚地知道每个字段代表什么:

  • $remote_addr: 客户端 IP 地址(192.168.1.100)
  • $remote_user: 远程用户(用于 HTTP 认证,通常为-
  • [$time_local]: 请求时间戳([10/Apr/2023:15:30:22 +0800])
  • "$request": 请求方法、URI 和协议("GET /api/user?id=123 HTTP/1.1")
  • $status: HTTP 状态码(200)
  • $body_bytes_sent: 发送给客户端的正文字节数(1534)
  • "$http_referer": 来源页面("https://example.com/")
  • "$http_user_agent": 用户代理字符串,即浏览器或爬虫标识

关键一步:确认你的 Nginx 实际使用的日志格式。执行nginx -T命令(需要 Nginx 有-T参数支持)或直接查看你的 Nginx 配置文件(通常在/etc/nginx/nginx.conf/usr/local/nginx/conf/nginx.conf),找到access_log指令和它引用的log_format。务必确保你的脚本解析逻辑与实际的日志格式严格对应,否则字段会错乱。我的脚本默认按combined格式解析,这也是最通用的格式。

注意:有些配置可能使用了自定义的log_format,比如加入了$request_time(请求处理时间)、$upstream_response_time(上游响应时间)等非常有价值的字段。如果你的日志里有这些字段,强烈建议在脚本中利用起来,它们对于分析接口性能至关重要。

3. 脚本核心功能设计与模块拆解

一个有用的日志分析脚本,不应该只是简单统计行数。我把它设计成模块化的,每个模块负责一个维度的分析,最后可以汇总输出。这样也方便你后续增删功能。核心模块包括:

  1. 流量概览:总请求数、独立 IP 数、总流量消耗。
  2. 状态码分析:统计各类 HTTP 状态码(2xx, 3xx, 4xx, 5xx)的数量和比例,快速发现错误。
  3. 热门请求分析:找出访问量最高的 URL(或接口),有助于发现热点或潜在的攻击路径。
  4. 客户端分析:统计访问最频繁的 IP 地址(可能是正常用户,也可能是爬虫或攻击源)。
  5. 用户代理分析:识别主要的浏览器、爬虫(如 Googlebot, Baiduspider)或异常客户端。
  6. 耗时分析(如果日志包含时间字段):统计平均响应时间,找出最慢的请求。
  7. 报告输出与告警:将分析结果格式化成易读的报告,输出到屏幕、文件,或通过邮件发送。可以设置阈值,当错误率或慢请求超过一定比例时触发告警。

基于这些模块,脚本的基本工作流程是:

  1. 指定要分析的日志文件路径(可以是单个文件,也可以是按天切割的文件,如access.logaccess.log-20231015.gz)。
  2. 按行读取日志文件。
  3. 使用awk按预定义的格式拆分每一行日志,提取出各个字段。
  4. 根据不同的分析维度,对提取出的字段进行聚合计算(计数、求和、排序等)。
  5. 将各模块的分析结果汇总,格式化输出。

下面,我们进入具体的实现环节。

4. 基础 Shell 脚本实现与逐行解析

我们先实现一个最基础的、纯 Shell (bash) 版本的脚本。它不依赖外部语言,在任何标准的 Linux 环境(包括 openEuler)中都能直接运行。

创建一个名为nginx_log_analyzer.sh的文件:

#!/bin/bash # 基础版 Nginx 日志分析脚本 # 适用于 combined 日志格式 LOG_FILE="${1:-/var/log/nginx/access.log}" # 允许通过参数传入日志路径,默认使用 /var/log/nginx/access.log if [ ! -f "$LOG_FILE" ]; then echo "错误:日志文件不存在 - $LOG_FILE" exit 1 fi echo "==============================================" echo "Nginx 访问日志分析报告" echo "分析文件: $LOG_FILE" echo "生成时间: $(date '+%Y-%m-%d %H:%M:%S')" echo "==============================================" echo ""

4.1 流量概览模块实现

这个模块我们统计三个基础指标。

# 模块1: 流量概览 echo "1. 流量概览" echo "----------------------------------------------" # 总请求数:直接计算日志行数(排除可能的空行) TOTAL_REQUESTS=$(grep -c "^" "$LOG_FILE" 2>/dev/null || wc -l < "$LOG_FILE") echo " 总请求数: $TOTAL_REQUESTS" # 独立IP数:提取第一列(remote_addr),去重计数 UNIQUE_IPS=$(awk '{print $1}' "$LOG_FILE" | sort | uniq | wc -l) echo " 独立IP地址数: $UNIQUE_IPS" # 总流出流量:求和第10列(body_bytes_sent)。注意:combined格式下,body_bytes_sent通常是第10列。 # 使用awk进行求和,效率远高于在Shell循环中累加。 TOTAL_BYTES=$(awk '{sum+=$10} END {print sum}' "$LOG_FILE") # 将字节转换为更易读的单位 (KB, MB, GB) if [ "$TOTAL_BYTES" -gt 1073741824 ]; then TRAFFIC_READABLE=$(echo "scale=2; $TOTAL_BYTES/1073741824" | bc) echo " 总流出流量: ${TRAFFIC_READABLE} GB" elif [ "$TOTAL_BYTES" -gt 1048576 ]; then TRAFFIC_READABLE=$(echo "scale=2; $TOTAL_BYTES/1048576" | bc) echo " 总流出流量: ${TRAFFIC_READABLE} MB" elif [ "$TOTAL_BYTES" -gt 1024 ]; then TRAFFIC_READABLE=$(echo "scale=2; $TOTAL_BYTES/1024" | bc) echo " 总流出流量: ${TRAFFIC_READABLE} KB" else echo " 总流出流量: ${TOTAL_BYTES} Bytes" fi echo ""

实操心得:在求和总流量时,使用awk的内置数值计算{sum+=$10}比在 Shell 里用while read循环累加快得多,尤其是处理百万行日志时,性能差异非常明显。另外,bc命令用于浮点数计算,如果系统未安装,可以用dnf install bc -y安装,或者用printf进行格式转换。

4.2 状态码分析模块实现

HTTP 状态码是衡量服务健康度的晴雨表。

# 模块2: HTTP 状态码分析 echo "2. HTTP 状态码分布" echo "----------------------------------------------" # 使用awk提取第9列(status),然后排序、计数、按数量降序排列 awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -20 | while read count code; do # 计算百分比 if [ "$TOTAL_REQUESTS" -ne 0 ]; then percentage=$(echo "scale=2; $count*100/$TOTAL_REQUESTS" | bc) # 根据状态码范围添加简单描述 case $code in 2*) desc="成功" ;; 3*) desc="重定向" ;; 4*) desc="客户端错误" ;; 5*) desc="服务器错误" ;; *) desc="其他" ;; esac printf " %-6s: %-6d 次 (%-5s%% ) [%s]\n" "$code" "$count" "$percentage" "$desc" fi done # 单独统计4xx和5xx错误的总数,便于监控 ERROR_4XX=$(awk '$9 ~ /^4[0-9]{2}$/ {count++} END {print count+0}' "$LOG_FILE") ERROR_5XX=$(awk '$9 ~ /^5[0-9]{2}$/ {count++} END {print count+0}' "$LOG_FILE") echo " 4xx 客户端错误总计: $ERROR_4XX" echo " 5xx 服务器错误总计: $ERROR_5XX" if [ "$TOTAL_REQUESTS" -ne 0 ]; then ERROR_4XX_RATIO=$(echo "scale=4; $ERROR_4XX/$TOTAL_REQUESTS*100" | bc) ERROR_5XX_RATIO=$(echo "scale=4; $ERROR_5XX/$TOTAL_REQUESTS*100" | bc) echo " 4xx 错误率: ${ERROR_4XX_RATIO}%" echo " 5xx 错误率: ${ERROR_5XX_RATIO}%" fi echo ""

注意事项uniq -c命令会在每行前面加上出现次数,sort -rn是按数字逆序排序(出现次数多的在前)。head -20只显示前20种状态码,通常足够了。这里用while read循环来格式化输出,并计算百分比。注意在计算百分比时,要判断分母(总请求数)是否为0,否则bc命令会出错。

4.3 热门请求与客户端分析模块实现

找出“最忙”的接口和最“活跃”的客户端。

# 模块3: 热门请求URL (TOP 20) echo "3. 热门请求 URL (TOP 20)" echo "----------------------------------------------" # 提取第7列(request中的URI部分,如 /api/user)。先按$7排序计数,再按频率排序。 awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -20 | while read count url; do if [ "$TOTAL_REQUESTS" -ne 0 ]; then percentage=$(echo "scale=2; $count*100/$TOTAL_REQUESTS" | bc) printf " %-50s: %-6d 次 (%-5s%% )\n" "$url" "$count" "$percentage" fi done echo "" # 模块4: 活跃客户端IP (TOP 20) echo "4. 活跃客户端 IP (TOP 20)" echo "----------------------------------------------" awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -20 | while read count ip; do if [ "$TOTAL_REQUESTS" -ne 0 ]; then percentage=$(echo "scale=2; $count*100/$TOTAL_REQUESTS" | bc) # 可以尝试解析IP归属地(需要额外工具或API,这里仅显示IP) printf " %-20s: %-6d 次 (%-5s%% )\n" "$ip" "$count" "$percentage" fi done echo ""

4.4 用户代理分析模块实现

用户代理字符串能告诉我们访问者是谁。

# 模块5: 用户代理分析 (TOP 15) echo "5. 主要用户代理 (TOP 15)" echo "----------------------------------------------" # 提取最后一列(User-Agent),进行粗略归类 awk -F'"' '{print $6}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -15 | while read count agent; do # 简化过长的UA字符串 SHORT_AGENT=$(echo "$agent" | cut -c1-80) printf " %-80s: %d 次\n" "$SHORT_AGENT" "$count" done echo ""

至此,一个基础功能的纯 Shell 分析脚本就完成了。给它加上执行权限chmod +x nginx_log_analyzer.sh,然后运行./nginx_log_analyzer.sh /path/to/your/access.log试试看吧。对于几十兆的日志文件,它能在几秒内给出结果。

5. 进阶:增强版脚本与性能优化

基础版脚本虽然能用,但还有不少可以增强的地方。比如,处理压缩日志(.gz)、分析自定义日志字段(如响应时间)、生成更美观的报告(HTML)、定时任务与邮件发送等。我们一步步来。

5.1 支持压缩日志与多文件输入

生产环境的日志通常会按天切割并压缩,例如access.log-20231015.gz。我们可以让脚本自动识别并处理。

修改脚本开头,使其能接受通配符或文件列表:

#!/bin/bash # 增强版:支持处理多个日志文件(包括.gz压缩格式) LOG_FILES=("${@:-/var/log/nginx/access.log}") # 支持传入多个文件,如 ./script.sh access.log*.gz TMP_COMBINED_LOG="/tmp/nginx_analysis_combined.log.$$" # 使用进程ID创建临时文件,避免冲突 # 清空或创建临时文件 > "$TMP_COMBINED_LOG" echo "正在处理日志文件..." for file in "${LOG_FILES[@]}"; do if [ ! -e "$file" ]; then echo "警告:文件不存在,跳过 - $file" continue fi echo " -> 处理: $file" # 根据文件后缀决定解压方式 case "$file" in *.gz) zcat "$file" >> "$TMP_COMBINED_LOG" 2>/dev/null || gzip -dc "$file" >> "$TMP_COMBINED_LOG" ;; *.bz2) bzcat "$file" >> "$TMP_COMBINED_LOG" 2>/dev/null || bzip2 -dc "$file" >> "$TMP_COMBINED_LOG" ;; *.xz) xzcat "$file" >> "$TMP_COMBINED_LOG" 2>/dev/null || xz -dc "$file" >> "$TMP_COMBINED_LOG" ;; *) cat "$file" >> "$TMP_COMBINED_LOG" ;; esac done # 检查临时文件是否有内容 if [ ! -s "$TMP_COMBINED_LOG" ]; then echo "错误:未找到有效的日志数据。" rm -f "$TMP_COMBINED_LOG" exit 1 fi # 后续的分析都基于这个临时合并文件 ANALYSIS_FILE="$TMP_COMBINED_LOG"

在脚本最后,记得清理临时文件:

# 脚本结尾处添加清理代码 rm -f "$TMP_COMBINED_LOG" echo "分析完成。临时文件已清理。"

踩坑记录:使用zcat,bzcat,xzcat这些命令时,要确保系统已经安装了对应的工具(gzip,bzip2,xz-utils)。在 openEuler 上,可以用dnf install gzip bzip2 xz -y来安装。另外,临时文件命名加上进程ID ($$) 是个好习惯,可以防止多个脚本实例同时运行时文件冲突。

5.2 集成响应时间分析(如果日志包含)

如果你的 Nginx 配置了$request_time$upstream_response_time到日志中,这个数据就太宝贵了。假设你的log_formatcombined基础上增加了$request_time作为第11列。

首先,修改log_format并重载 Nginx:

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

然后,在脚本中添加分析模块:

# 模块6: 请求耗时分析 (需要日志包含$request_time字段,假设在第11列) echo "6. 请求耗时分析 (单位: 秒)" echo "----------------------------------------------" # 检查日志是否包含足够多的列(至少11列) SAMPLE_LINE=$(head -1 "$ANALYSIS_FILE") COLUMN_COUNT=$(echo "$SAMPLE_LINE" | awk '{print NF}') if [ "$COLUMN_COUNT" -ge 11 ]; then # 计算平均响应时间 AVG_TIME=$(awk '{sum+=$11} END {if(NR>0) print sum/NR; else print 0}' "$ANALYSIS_FILE") printf " 平均响应时间: %.3f 秒\n" "$AVG_TIME" # 统计耗时区间的请求数量 echo " 响应时间分布:" awk '{rt=$11; if(rt<0.1) a++; else if(rt<0.5) b++; else if(rt<1.0) c++; else if(rt<3.0) d++; else e++;} END { total=NR; printf " <0.1s : %d (%.1f%%)\n", a, (a/total)*100; printf " 0.1-0.5s: %d (%.1f%%)\n", b, (b/total)*100; printf " 0.5-1s : %d (%.1f%%)\n", c, (c/total)*100; printf " 1-3s : %d (%.1f%%)\n", d, (d/total)*100; printf " >3s : %d (%.1f%%)\n", e, (e/total)*100; }' "$ANALYSIS_FILE" # 找出最慢的10个请求 echo "" echo " 最慢的请求 (TOP 10):" # 打印:耗时、IP、方法、URL、状态码 awk '{printf "%.3f %s %s %s %s\n", $11, $1, $6, $7, $9}' "$ANALYSIS_FILE" | sort -rn | head -10 | while read time ip method url status; do # 清理method中的引号 method=$(echo "$method" | tr -d '"') printf " %-8s %-15s %-6s %-40s %s\n" "${time}s" "$ip" "$method" "$url" "$status" done else echo " 当前日志格式未包含响应时间字段(request_time)。" echo " 建议在nginx.conf的log_format中添加 \$request_time 字段以启用此分析。" fi echo ""

这个模块能让你一眼看出服务的性能瓶颈在哪里,哪些接口最慢,慢请求来自哪些IP。

5.3 生成 HTML 报告与邮件发送

纯文本报告在终端看还行,但要是想每天定时发邮件给团队,HTML 格式就友好多了。我们可以用 Shell 生成一个简单的 HTML 页面,或者更优雅一点,用 Python 来生成。

这里提供一个混合方案:用 Shell 收集数据,然后用 Python 的jinja2模板(或直接字符串拼接)生成 HTML。为了简化,我们先用 Shell 生成一个简单的 HTML 片段。

首先,在脚本中,将每个模块的输出不仅打印到屏幕,也重定向到一个变量或文件。这里我们选择将关键数据存储到变量中,最后统一渲染。

但更实用的方法是:将 Shell 脚本作为数据收集器,然后调用一个 Python 脚本来生成报告和发邮件。我们可以在 Shell 脚本末尾添加:

# 假设我们将关键数据以JSON格式输出到一个临时文件 TMP_DATA_JSON="/tmp/nginx_analysis_data.json.$$" cat > "$TMP_DATA_JSON" <<EOF { "total_requests": $TOTAL_REQUESTS, "unique_ips": $UNIQUE_IPS, "total_traffic_bytes": $TOTAL_BYTES, "error_4xx": $ERROR_4XX, "error_5xx": $ERROR_5XX, "error_4xx_ratio": $ERROR_4XX_RATIO, "error_5xx_ratio": $ERROR_5XX_RATIO, "top_ips": [ $(awk '{print $1}' "$ANALYSIS_FILE" | sort | uniq -c | sort -rn | head -5 | awk '{printf "{\"ip\": \"%s\", \"count\": %s},\n", $2, $1}' | sed '$s/,$//') ], "top_urls": [ $(awk '{print $7}' "$ANALYSIS_FILE" | sort | uniq -c | sort -rn | head -5 | awk '{printf "{\"url\": \"%s\", \"count\": %s},\n", $2, $1}' | sed '$s/,$//') ] } EOF # 然后调用 Python 脚本处理这个 JSON 并生成报告 PYTHON_REPORT_SCRIPT="/path/to/your/generate_report.py" if [ -f "$PYTHON_REPORT_SCRIPT" ] && command -v python3 &>/dev/null; then python3 "$PYTHON_REPORT_SCRIPT" --input-json "$TMP_DATA_JSON" --log-file "$LOG_FILE" --output-html "/tmp/report_$(date +%Y%m%d).html" # 可以在这里添加发送邮件的命令,例如使用 mailx 或 python 的 smtplib # mailx -s "Nginx日志日报 $(date +%Y-%m-%d)" -a "/tmp/report_$(date +%Y%m%d).html" team@example.com < /dev/null else echo "Python 报告脚本未找到或 Python3 未安装,跳过 HTML 报告生成。" fi # 清理临时数据文件 rm -f "$TMP_DATA_JSON"

generate_report.py可以是一个简单的 Python 脚本,使用json模块加载数据,用字符串模板或jinja2生成漂亮的 HTML。这里限于篇幅,不展开完整的 Python 代码,但思路是清晰的:数据由高效的 Shell 脚本预处理,展示层由更灵活的 Python 负责。

5.4 性能优化技巧

当单日日志文件达到 GB 级别时,脚本的性能就很重要了。

  1. 避免多次读取文件:基础脚本中,每个awk或管道命令都会重新读取一遍日志文件。对于大文件,这是巨大的 IO 开销。优化方法是将日志预处理到一个临时文件,或者使用更复杂的awk脚本在一次读取中完成所有统计。我们上面使用的合并临时文件法,已经是一种优化,确保后续分析只读一次这个临时文件。
  2. 使用awk关联数组:在awk中,可以使用关联数组(即字典)在内存中完成计数和聚合,这是最快的文本处理方式之一。例如,统计状态码和URL可以合并到一个awk脚本里。
  3. 处理压缩文件时zcat是流式读取,不会完全解压到磁盘,比先gzip -dcat更高效。
  4. 排序的代价sortuniq -c对内存要求较高。如果数据量极大,可以考虑使用LC_ALL=C sort来加速(设置区域为 C,使用字节排序,速度更快),或者使用awk的数组排序功能部分替代。
  5. 采样分析:对于海量历史日志,有时不需要100%的精确度。可以随机采样一部分行进行分析,用shuf命令(如果支持)或awk 'NR % 100 == 0'(每100行取一行)来减少数据量。

一个高度优化的、单次awk遍历完成多项统计的脚本框架如下(示意):

awk ' { # 提取字段 ip = $1 status = $9 url = $7 bytes = $10 # 假设是第10列 # 1. 统计总请求和流量 total_req++ total_bytes += bytes # 2. 统计独立IP (近似,完全精确需要存储所有IP,内存可能不够) ip_count[ip]++ # 3. 统计状态码 status_count[status]++ # 4. 统计URL url_count[url]++ # ... 其他统计 } END { # 输出所有结果 print "总请求数:", total_req print "总流量:", total_bytes # 遍历数组输出TOP N # 注意:awk中排序数组比较麻烦,通常还是输出到外部用sort排序 for (s in status_count) { print s, status_count[s] > "/tmp/status_stats.txt" } # ... 其他输出 } ' "$LOG_FILE"

6. 部署与自动化:定时任务与告警集成

脚本写好了,总不能每次都手动运行。我们需要把它集成到系统的自动化流程中。

6.1 配置 Crontab 定时任务

假设我们的完整脚本路径是/opt/scripts/nginx_log_analyzer.sh,并且它支持处理压缩日志。

编辑当前用户的 crontab:crontab -e

添加以下行,实现每天凌晨1点分析前一天的日志,并发送邮件报告:

# 每天凌晨1点10分执行日志分析 10 1 * * * /opt/scripts/nginx_log_analyzer.sh /var/log/nginx/access.log-$(date -d "yesterday" +\%Y\%m\%d).gz > /var/log/nginx_analysis.log 2>&1 # 如果日志是按天切割但未压缩,去掉 .gz # 10 1 * * * /opt/scripts/nginx_log_analyzer.sh /var/log/nginx/access.log-$(date -d "yesterday" +\%Y\%m\%d) > /var/log/nginx_analysis.log 2>&1

注意:crontab 中的%需要转义为\%$(date -d "yesterday" +\%Y\%m\%d)会生成昨天的日期,如20231015。这要求你的 Nginx 日志切割命名规则与之匹配。常见的日志切割工具如logrotate配置的命名可能是access.log-YYYYMMDD

6.2 集成简单告警

我们可以在脚本中增加判断逻辑,当某些指标超过阈值时,不仅输出报告,还发送告警通知(如邮件、钉钉、企业微信等)。

在 Shell 脚本的末尾,添加如下告警判断:

# 告警阈值配置 ALERT_5XX_RATIO=1 # 5xx错误率超过1%告警 ALERT_AVG_TIME=2 # 平均响应时间超过2秒告警 (如果启用了耗时分析) # 判断并告警 ALERT_MESSAGE="" if [ $(echo "$ERROR_5XX_RATIO > $ALERT_5XX_RATIO" | bc) -eq 1 ]; then ALERT_MESSAGE="${ALERT_MESSAGE}⚠️ 5xx错误率过高: ${ERROR_5XX_RATIO}% (阈值: ${ALERT_5XX_RATIO}%)\n" fi if [ -n "$AVG_TIME" ] && [ $(echo "$AVG_TIME > $ALERT_AVG_TIME" | bc) -eq 1 ]; then ALERT_MESSAGE="${ALERT_MESSAGE}⚠️ 平均响应时间过长: ${AVG_TIME}秒 (阈值: ${ALERT_AVG_TIME}秒)\n" fi if [ -n "$ALERT_MESSAGE" ]; then echo -e "\n【告警】\n$ALERT_MESSAGE" # 这里可以集成发送告警邮件的命令,例如: # echo -e "Subject: Nginx服务告警 - $(date)\n\n$ALERT_MESSAGE" | sendmail -v admin@example.com # 或者调用发送钉钉/企业微信的脚本 # /opt/scripts/send_dingding_alert.sh "Nginx服务异常" "$ALERT_MESSAGE" fi

6.3 日志文件路径与权限问题

在 openEuler 或其他 Linux 系统上,Nginx 日志默认通常由rootnginx用户创建,权限可能是640。如果你的脚本由普通用户(比如通过 crontab)执行,可能会因为权限不足无法读取日志。

解决方法有几种:

  1. 最佳实践:将执行脚本的用户(如nginx或一个专用的监控用户)加入nginxroot组,并调整日志文件的组权限为可读。
    sudo usermod -a -G nginx your_username sudo chmod 644 /var/log/nginx/access.log # 注意,这可能会降低安全性,需权衡 # 或者更精细地设置组权限 sudo chown root:nginx /var/log/nginx/access.log sudo chmod 640 /var/log/nginx/access.log
    然后,执行脚本的用户需要重新登录以使组生效。
  2. 使用 sudo:在 crontab 中通过sudo以 root 权限运行脚本。但这需要配置sudoers文件,赋予相应用户无需密码运行该脚本的权限,存在安全风险,需谨慎。
  3. 修改日志目录权限:不推荐,会降低系统安全性。

7. 常见问题排查与实战技巧

在实际操作中,你可能会遇到下面这些问题。

7.1 脚本执行报错:字段错乱或空白

症状:统计出的 URL 全是乱码,或者 IP 地址跑到状态码那一列去了。原因:日志格式与脚本中awk的列索引不匹配。combined格式是固定的,但如果你自定义了log_format,列的顺序就变了。排查

  1. 查看一条真实的日志样本:head -1 /var/log/nginx/access.log
  2. 数一下每个字段的位置。注意,$request(如"GET /foo HTTP/1.1")是一个整体算一列,即使里面包含空格。
  3. 调整脚本中awk的列号。例如,如果$request_time加在了最后,它可能是第11列;如果加在了中间,后面的列号都要顺延。

7.2 处理海量日志时脚本卡死或内存不足

症状:脚本运行很久不出结果,或者直接被系统 kill 掉。原因sortuniq在处理超大文件时需要大量内存和临时磁盘空间。优化

  1. 使用LC_ALL=C:在sort前加上LC_ALL=C,可以大幅提升排序速度,因为它使用简单的字节排序而非复杂的本地化规则。LC_ALL=C sort ...
  2. 指定临时目录:如果/tmp空间小,可以用-T参数指定到大容量的分区:sort -T /bigdisk/tmp ...
  3. 分而治之:如果日志是按小时或按天切割的,分别分析每个小文件,然后再汇总结果。
  4. 采样分析:如前所述,使用awk 'NR % 100 == 0'进行采样。
  5. 换用更高效的工具:对于 TB 级别的日志,考虑使用GoRust写一个专门的分析程序,或者直接上ELKClickHouse这类专业方案。

7.3 分析结果中 URL 带有查询参数,导致统计分散

症状/api/user?id=1/api/user?id=2被算作两个不同的 URL,无法聚合。解决:在统计前,用awksed清洗掉查询字符串。例如,只取$7(URI)中?之前的部分:

# 在提取URL的awk命令中处理 awk '{split($7, a, "?"); url=a[1]; print url}' "$LOG_FILE" | sort | uniq -c | sort -rn

split($7, a, "?")将第7列以?为分隔符拆分成数组aa[1]就是问号前的路径部分。

7.4 如何分析 POST 请求的请求体大小?

Nginx 的$body_bytes_sent记录的是发送给客户端的响应体大小,不是客户端发来的请求体大小。Nginx 默认的访问日志不记录客户端请求体大小。如果你需要记录,可以在log_format中添加$request_length变量,它表示客户端请求的总长度(包括请求行、头部和请求体)。但请注意,这可能会略微增加日志体积和性能开销。

7.5 在 openEuler 上邮件发送失败

如果你使用mailx通过外部 SMTP 服务器发信,可能需要配置/etc/mail.rc~/.mailrc。一个简单的配置示例(使用 Gmail 的 SMTP,请注意安全风险,不建议在生产环境用密码):

set smtp=smtp.gmail.com:587 set smtp-use-starttls=yes set ssl-verify=ignore set nss-config-dir=/etc/pki/nssdb/ set smtp-auth=login set smtp-auth-user=your-email@gmail.com set smtp-auth-password=your-app-password # 注意:不要用真实密码,用应用专用密码 set from="Your Name <your-email@gmail.com>"

在 openEuler 上,可能需要安装mailxs-nail变体并确保其支持 SMTP。更可靠的方式是使用 Python 的smtplib库来发送邮件,灵活性更高。

8. 扩展思路:从脚本到简易日志分析平台

这个脚本是一个很好的起点。随着需求增长,你可以考虑以下扩展方向,逐步构建一个轻量级的内部日志分析平台:

  1. 数据持久化:将脚本每日的分析结果(如总请求数、错误率、TOP IP等)写入一个简单的数据库(如 SQLite)或文本文件(CSV)。这样你就可以绘制趋势图,观察指标随时间的变化。
  2. 可视化:用 Python 的matplotlibplotly库,读取持久化的数据,生成每日/每周的趋势图表,并嵌入到 HTML 报告中。
  3. 实时监控:使用tail -f命令实时读取 Nginx 日志,并通过管道传递给一个简单的实时分析脚本(可以用awkPython),实时计算 QPS、错误率,并在控制台动态刷新或推送至监控面板。
  4. 集成更多日志源:将脚本模块化,使其不仅能分析 Nginx 访问日志,还能分析错误日志(error.log)、应用日志、数据库慢查询日志等。为每种日志定义不同的解析规则和分析模板。
  5. 自动化根因分析(RCA):当脚本检测到错误率飙升时,可以自动关联分析同一时间段内的错误日志、系统监控指标(如 CPU、内存),甚至调用一些诊断命令,尝试给出可能的原因提示。这需要更复杂的逻辑,但可以极大提升排障效率。

最终,这个始于简单 Shell 脚本的项目,可以根据你的实际需求,演变成一套高度定制化、成本可控的日志监控解决方案。它可能没有商业 APM 工具那么功能全面,但它完全贴合你的业务,没有冗余功能,而且所有数据都在自己掌控之中。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 22:21:10

基于A89307和PIC18F66K40的BLDC电机FOC控制方案

1. 项目背景与核心需求在工业自动化、无人机和电动汽车等领域&#xff0c;无刷直流电机&#xff08;BLDC&#xff09;因其高效率、长寿命和低噪音等优势&#xff0c;正逐步取代传统有刷电机。然而&#xff0c;实现高性能的BLDC控制并非易事&#xff0c;尤其是当需要处理高达15A…

作者头像 李华
网站建设 2026/7/5 22:20:28

单光子探测器成像技术原理与3D重建实践

1. 单光子探测器成像技术概述 单光子探测器成像技术代表了当前光学成像领域的前沿发展方向。这项技术的核心在于能够探测单个光子级别的光信号&#xff0c;并通过先进的计算成像算法重建出高质量的3D图像。与传统成像系统相比&#xff0c;单光子探测器在光子利用效率方面具有显…

作者头像 李华
网站建设 2026/7/5 22:20:18

Seedance 2.0/2.5全网出圈,HappyHorse(快乐马)却默默无闻的6层核心原因

Seedance 2.0/2.5全网出圈,HappyHorse(快乐马)却默默无闻的6层核心原因 一、发布与宣发策略天差地别(最核心差距) Seedance:全渠道、强曝光、持续迭代造势 正规重磅发布会 Seedance 2.0在字节官方豆包技术博客正式官宣;2.5直接放在火山FORCE年度AI大会主舞台发布,配套…

作者头像 李华
网站建设 2026/7/5 22:17:59

AI创意工作流深度解析:MiniMax Hub如何重塑内容创作与设计流程

&#x1f680; 30款热门AI模型一站整合&#xff0c;DeepSeek/GLM/Qwen 随心用&#xff0c;限时 5 折。 &#x1f449; 点击领海量免费额度 这次我们来看一个面向创意工作的AI工具——MiniMax Hub。它被描述为“创意工作的Claude Code”&#xff0c;这个定位很有意思。Claude…

作者头像 李华
网站建设 2026/7/5 22:14:57

深度学习革命:从AlexNet到现代CNN架构演进

1. 深度学习大爆发的时代背景 2012年之前的人工智能领域就像一位拥有绝妙设计理念的建筑师被困在石器时代。科学家们早已在理论上构建了神经网络的基本框架&#xff0c;但受限于当时的计算能力和数据规模&#xff0c;这些理论模型就像用泥巴和树枝搭建的摩天大楼——理念先进却…

作者头像 李华
网站建设 2026/7/5 22:14:26

Bankrate暂停AI内容:金融领域AI生成内容的合规红线与信任危机

1. 项目概述&#xff1a;一场被公开叫停的AI内容实验 Bankrate——这家成立于1976年、以银行利率比价起家的老牌金融信息平台&#xff0c;2023年中曾高调启动一项内部代号为“Project Atlas”的AI内容生成计划。他们不是简单地用AI写几篇测试稿&#xff0c;而是真金白银投入&am…

作者头像 李华