0Pricing
Linux Command Line & Bash Scripting Mastery · 课时

实时跟踪日志与流式告警

实时跟踪并筛选日志流,在错误模式出现的瞬间触发告警。

实时跟踪日志与流式告警 是 CoddyKit 上的免费 Linux Command Line & Bash Scripting Mastery 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Linux Command Line & Bash Scripting Mastery 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。

实时跟踪日志为何重要

在生产系统中,日志文件会持续增长。等到有人报告问题后才检查日志,意味着停机损失已经发生。实时日志跟踪可以让您在事件写入的瞬间观察其发生过程,从而立即响应故障、安全事件和性能下降。

  • Web 服务器每个请求写入一行日志 — 错误会立即出现
  • 应用程序守护进程会在异常触发的瞬间记录堆栈跟踪
  • 身份验证系统会实时记录登录失败尝试

Unix 中用于此目的的基础工具是 tail -f,结合筛选和告警工具,可以将原始日志流转换为可采取行动的信号。

tail -f:跟踪实时日志

tail -f(跟踪)会保持文件打开,并在新行追加时将其打印出来。这是最简单、最普遍可用的实时日志工具。

常见用法:

  • tail -f /var/log/syslog — 跟踪系统日志
  • tail -n 50 -f app.log — 显示最后 50 行,然后继续跟踪
  • tail -f /var/log/nginx/access.log — 实时跟踪 nginx 请求

按 Ctrl+C 停止。除非您取消操作或终端关闭,否则该进程会一直保持连接。

#!/usr/bin/env bash
# Simulate a live log and follow it
LOGFILE='/tmp/demo_app.log'

# Write a header
echo '[INFO] Application started' >> "$LOGFILE"

# In a real scenario you would run:
# tail -f "$LOGFILE"
# Below we demonstrate tail showing the last 3 lines then exit
tail -n 3 "$LOGFILE"

tail -F:应对日志轮换

许多系统会在午夜或日志文件达到大小限制时轮换日志文件。发生这种情况时,原文件会被重命名(例如 app.log.1),并创建新的 app.log。使用 tail -f 时,您会悄悄地继续读取旧的重命名文件,从而错过所有新的输出。

tail -F(大写 F)通过监视文件名而不是文件描述符解决了这个问题。当文件消失后重新出现时,tail -F 会自动重新打开它并继续跟踪。

  • 在生产脚本中,应始终优先使用 tail -F,而不是 tail -f
  • 适用于会轮换文件的 logrotate、newsyslog 和 Docker 日志驱动程序
# Follow nginx access log, surviving log rotation
tail -F /var/log/nginx/access.log

# Follow multiple files simultaneously
tail -F /var/log/nginx/access.log /var/log/nginx/error.log

使用 grep 筛选日志流

直接跟踪繁忙的日志会让人难以应付 — 活跃的 Web 服务器每秒可能写入数百行。将 tail -F 的输出通过管道传给 grep,即可只保留您关注的模式。

流式 grep 的关键选项:

  • --line-buffered — 立即刷新每个匹配行,而不是进行缓冲;在流水线中必须使用,否则输出可能延迟或丢失
  • -i — 不区分大小写地匹配
  • -E — 使用扩展正则表达式进行交替匹配(error|warn|crit)
  • -v — 反向匹配(排除行)
# Show only ERROR and WARN lines from a live application log
tail -F /var/log/myapp/app.log | grep --line-buffered -Ei 'error|warn|critical'

# Follow nginx and exclude health-check requests
tail -F /var/log/nginx/access.log | grep --line-buffered -v '/health'

使用 awk 添加时间戳和上下文

日志行有时缺少有助于初步排查的上下文。您可以使用 awk 实时丰富日志流 — 添加本地时间戳、提取字段,或重新格式化输出以提高可读性。

通过管道使用时,awk 也会以流式(按行缓冲)模式运行,因此无需额外选项即可安全地用于实时流水线。

# Prepend a reception timestamp to every ERROR line
tail -F /var/log/myapp/app.log | \
  grep --line-buffered -i 'error' | \
  awk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0; fflush() }'

# Extract HTTP status code (field 9) and URL (field 7) from nginx combined log
tail -F /var/log/nginx/access.log | \
  awk '{ print $9, $7; fflush() }' | \
  grep --line-buffered '^5'

使用 Slack Webhook 发送告警

筛选只是工作的一半 — 检测到错误模式后,您还需要通知相关人员。Slack 入站 Webhook 只需调用一次 curl,就能向频道 POST 一条消息;除了 Webhook URL 外,不需要 Slack SDK 或其他凭据。

处理模式是:筛选日志流,并为每个匹配行发送一次 HTTP POST。

#!/usr/bin/env bash
# Real-time alert: send every ERROR line to a Slack channel
LOGFILE='/var/log/myapp/app.log'
WEBHOOK_URL='https://hooks.slack.com/services/T000/B000/XXXX'

tail -F "$LOGFILE" | grep --line-buffered -i 'error' | while IFS= read -r line; do
  payload=$(printf '{"text":"*[ERROR ALERT]*\n%s"}' "$line")
  curl -s -o /dev/null -X POST -H 'Content-Type: application/json' \
    -d "$payload" "$WEBHOOK_URL"
done

限制告警频率,避免噪声

日志风暴每分钟可能产生数千行错误。每行发送一条 Slack 消息会淹没频道,并导致告警疲劳。您需要限制频率 — 触发一次告警后,在冷却期间抑制后续通知。

这可以通过一个简单的时间戳文件实现:记录上次发送告警的时间;如果冷却时间尚未结束,则跳过本次触发。

#!/usr/bin/env bash
# Alert on ERROR lines but no more than once every 60 seconds
LOGFILE='/var/log/myapp/app.log'
WEBHOOK_URL='https://hooks.slack.com/services/T000/B000/XXXX'
COOLDOWN=60
LAST_ALERT_FILE='/tmp/last_alert_ts'

tail -F "$LOGFILE" | grep --line-buffered -i 'error' | while IFS= read -r line; do
  now=$(date +%s)
  last=0
  [ -f "$LAST_ALERT_FILE" ] && last=$(cat "$LAST_ALERT_FILE")

  if (( now - last >= COOLDOWN )); then
    echo "$now" > "$LAST_ALERT_FILE"
    payload=$(printf '{"text":"*[ERROR ALERT]*\n%s"}' "$line")
    curl -s -o /dev/null -X POST -H 'Content-Type: application/json' \
      -d "$payload" "$WEBHOOK_URL"
    echo "[$(date)] Alert sent: $line"
  else
    echo "[$(date)] Suppressed (cooldown): $line"
  fi
done

使用滑动窗口统计错误突发

有时单行错误并不重要 — 但 30 秒内出现 20 个错误就是严重问题。滑动窗口计数器可以仅在错误速率超过阈值时触发告警,从而减少误报。

该技术会将每个匹配事件的纪元时间戳存储在临时文件中,然后统计落在窗口内的事件数量,再决定是否发送告警。

#!/usr/bin/env bash
# Alert when more than 10 errors occur within any 60-second window
LOGFILE='/var/log/myapp/app.log'
WINDOW=60
THRESHOLD=10
TS_FILE='/tmp/error_timestamps'
WEBHOOK_URL='https://hooks.slack.com/services/T000/B000/XXXX'

tail -F "$LOGFILE" | grep --line-buffered -i 'error' | while IFS= read -r line; do
  now=$(date +%s)
  echo "$now" >> "$TS_FILE"

  # Keep only timestamps within the window
  cutoff=$(( now - WINDOW ))
  tmp=$(mktemp)
  awk -v c="$cutoff" '$1 > c' "$TS_FILE" > "$tmp" && mv "$tmp" "$TS_FILE"

  count=$(wc -l < "$TS_FILE")
  if (( count > THRESHOLD )); then
    msg="*[BURST ALERT]* ${count} errors in ${WINDOW}s — last: ${line}"
    curl -s -o /dev/null -X POST -H 'Content-Type: application/json' \
      -d "{\"text\":\"$msg\"}" "$WEBHOOK_URL"
    # Clear to avoid re-alerting until next burst
    > "$TS_FILE"
  fi
done

journalctl -f:跟踪 systemd 日志

在现代 Linux 系统(RHEL、Ubuntu 20.04 及更高版本、Debian 10 及更高版本)中,服务会写入 systemd 日志,而不是普通文本文件。journalctl -f 相当于用于日志的 tail -F。

实用选项:

  • -u myapp.service — 仅跟踪指定单元
  • -p err — 按优先级筛选(emerg、alert、crit、err、warning、notice、info、debug)
  • --since '5 min ago' — 从相对时间点开始
  • -o json — 输出结构化 JSON,供机器解析
# Follow only error-and-above entries for nginx
journalctl -f -u nginx.service -p err

# Stream journal as JSON and extract MESSAGE field with jq
journalctl -f -u myapp.service -o json | \
  jq --unbuffered -r 'select(.PRIORITY <= "3") | .MESSAGE'

multitail 与彩色多源监控

当您需要同时监视多个日志源时,multitail 会将终端分割成多个窗格 — 每个窗格跟踪不同的文件或命令,并可根据模式使用不同颜色。

如果尚未安装 multitail,一种轻量的纯 BASH 替代方案是为每个日志流添加源名称前缀,然后将它们合并到一个视图中。

# multitail: watch nginx access + error + app log in split panes
# (requires: apt install multitail  or  brew install multitail)
multitail /var/log/nginx/access.log /var/log/nginx/error.log /var/log/myapp/app.log

# Pure-Bash alternative — merge three streams with labeled prefixes
(
  tail -F /var/log/nginx/access.log | sed --unbuffered 's/^/[nginx-access] /' &
  tail -F /var/log/nginx/error.log  | sed --unbuffered 's/^/[nginx-error]  /' &
  tail -F /var/log/myapp/app.log    | sed --unbuffered 's/^/[myapp]        /' &
  wait
)

构建自包含的告警守护进程

综合本课程所学内容,一个适用于生产环境的告警守护进程应当:

  • 使用 tail -F 稳健地跟踪日志文件
  • 使用带缓冲的 grep 筛选关键模式
  • 限制通知频率,避免告警疲劳
  • 记录自身活动,以便审计已发送的内容
  • 作为由 systemd 或监管进程管理的后台进程运行

下面的脚本是一个最小但完整的守护进程,您可以将其放入 /usr/local/bin/,并通过 systemd 管理。

#!/usr/bin/env bash
# log_alert_daemon.sh — tail a log and fire Slack alerts with cooldown
set -euo pipefail

LOGFILE=${1:-'/var/log/myapp/app.log'}
PATTERN=${2:-'error|critical|fatal'}
WEBHOOK_URL=${SLACK_WEBHOOK_URL:?'Set SLACK_WEBHOOK_URL env var'}
COOLDOWN=${ALERT_COOLDOWN:-120}
DAEMON_LOG='/var/log/log_alert_daemon.log'
LAST_SENT_FILE='/tmp/log_alert_last_sent'

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$DAEMON_LOG"; }

log "Starting alert daemon: watching $LOGFILE for pattern: $PATTERN"

tail -F "$LOGFILE" | grep --line-buffered -Ei "$PATTERN" | while IFS= read -r line; do
  now=$(date +%s)
  last=0
  [ -f "$LAST_SENT_FILE" ] && last=$(cat "$LAST_SENT_FILE")

  if (( now - last >= COOLDOWN )); then
    echo "$now" > "$LAST_SENT_FILE"
    msg=$(printf '{"text":"*[ALERT]* %s\n%s"}' "$(hostname)" "$line")
    if curl -s -o /dev/null -w '%{http_code}' -X POST \
         -H 'Content-Type: application/json' -d "$msg" "$WEBHOOK_URL" | grep -q '^200$'; then
      log "Alert sent: $line"
    else
      log "Alert FAILED to send: $line"
    fi
  else
    log "Suppressed (cooldown ${COOLDOWN}s): $line"
  fi
done

知识检查:流式日志流水线

测试您对实时日志跟踪和流式告警的理解。

一个 BASH 流水线跟踪日志文件,并为每个匹配行发送 Slack 告警。日志风暴期间,10 秒内写入了 3,000 行错误。哪一项单独的更改最能防止脚本向 Slack 频道发送 3,000 条消息?

课程回顾:实时日志跟踪与流式告警

在本课程中,您从基本原理出发,构建了一套完整的实时日志可观测性流水线:

  • tail -F 按名称跟踪日志文件,即使日志轮换也能继续工作 — 在生产环境中始终优先使用它,而不是 tail -f
  • grep --line-buffered 筛选实时流,同时不会引入延迟;在通过管道使用 grep 的命令中始终添加此选项
  • 使用 fflush() 的 awk 以适合流式处理的方式为每行添加时间戳或提取字段
  • 通过 curl 使用 Slack Webhook,通过单个 HTTP POST 发送告警 — 不需要 SDK
  • 冷却文件通过强制规定通知之间的最短间隔,避免日志风暴期间产生告警疲劳
  • 滑动窗口计数器检测错误突发(基于速率的告警),而不是对每一行单独作出反应
  • journalctl -f 是 systemd 原生的 tail -F 替代方案,内置优先级筛选和 JSON 输出
  • 自包含的告警守护进程脚本组合了所有这些模式,并可由 systemd 管理,以确保生产环境的可靠性

这些基本工具共同构成了任何自定义可观测性流水线的基础 — 不需要第三方代理。

常见问题解答

「实时跟踪日志与流式告警」课时是免费的吗?

是的 — 「实时跟踪日志与流式告警」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Linux Command Line & Bash Scripting Mastery 课程的其余内容,请升级到 CoddyKit PRO。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。

「实时跟踪日志与流式告警」这节课中我会学到什么?

实时跟踪并筛选日志流,在错误模式出现的瞬间触发告警。 你通过在浏览器中直接运行的动手代码来练习 Linux Command Line & Bash Scripting Mastery,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Linux Command Line & Bash Scripting Mastery 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Linux Command Line & Bash Scripting Mastery 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「实时跟踪日志与流式告警」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Linux Command Line & Bash Scripting Mastery 课中编写并运行代码吗?

能。每节 Linux Command Line & Bash Scripting Mastery 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 大规模解析 Web 与应用日志
  2. 实时跟踪日志与流式告警
  3. 在脚本中使用 journalctl 查询 journald
  4. 从日志流计算指标与直方图
← 返回 Linux Command Line & Bash Scripting Mastery