Skip to main content

monitor 输出到程序

你可以通过两种方式实现:tail -F 的输出通过管道喂给程序,或者在 Go/Python 代码中自己实现类似 tail -F 的逻辑。下面分别说明。


1. 管道方式:tail -F 输出重定向到程序的标准输入

这是最简单的方式,适合你已经想用 tail 命令本身,只需要把实时日志交给 Go/Python 处理。

Shell 中运行

tail -F /var/log/mysql/error.log | python3 process.py
tail -F /var/log/mysql/error.log | go run process.go

Python 接收示例 (process.py)

import sys

for line in sys.stdin:
line = line.strip()
if not line:
continue
# 处理每一行,例如提取死锁信息
print("Python got:", line)

Go 接收示例 (process.go)

package main

import (
"bufio"
"fmt"
"os"
)

func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
fmt.Println("Go got:", line)
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error reading stdin:", err)
}
}

注意tail -F 默认会缓冲输出,可能导致延迟。若需实时,可以使用 tail -F file | stdbuf -oL python3 process.pytail -F file | grep --line-buffered 强制行缓冲。


2. 原生实现:在 Go/Python 中实现 tail -F 逻辑

这种方式不需要依赖外部 tail 命令,程序自己就能持续跟踪文件新增内容,更适合封装成守护进程。

Python 实现(使用 open + seek + 循环)

import time
import os

def tail_f(filename, delay=0.5):
with open(filename, 'r') as f:
# 移到文件末尾
f.seek(0, os.SEEK_END)
while True:
line = f.readline()
if line:
yield line.strip()
else:
# 没有新行时检查文件是否被轮转(改名/重建)
if not os.path.exists(filename):
# 文件被删除/轮转,重新打开
f = open(filename, 'r')
f.seek(0, os.SEEK_END)
time.sleep(delay)

# 使用
for line in tail_f('/var/log/mysql/error.log'):
if line:
print("Python tail:", line)

Go 实现(使用 os.File + fsnotify 或简单轮询)

Go 标准库没有直接提供 tail -F,但可以借助 github.com/hpcloud/tail 库,或者自己实现轮询:

package main

import (
"bufio"
"fmt"
"os"
"time"
)

func tailF(filename string, delay time.Duration) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
// 跳到末尾
f.Seek(0, os.SEEK_END)

reader := bufio.NewReader(f)
for {
line, err := reader.ReadString('\n')
if err == nil {
fmt.Print(line)
continue
}
// 没有新行,等待并检查文件是否被轮转
time.Sleep(delay)
// 重新 stat 文件,判断 inode 是否变化
newF, err := os.Open(filename)
if err != nil {
continue
}
stat1, _ := f.Stat()
stat2, _ := newF.Stat()
if os.SameFile(stat1, stat2) {
newF.Close()
continue
}
// 文件被轮转了,使用新文件句柄
f.Close()
f = newF
f.Seek(0, os.SEEK_END)
reader = bufio.NewReader(f)
}
}

func main() {
tailF("/var/log/mysql/error.log", 500*time.Millisecond)
}

推荐:Go 生产环境可用 github.com/nxadm/tail 包,它实现了完整的 tail -F 语义(包括文件轮转、位置记录等)。


总结

方式优点缺点
管道 + tail -F简单、无需自己处理文件轮转依赖外部命令,跨平台性稍差
原生实现完全可控,无外部依赖需要自己处理文件轮转、错误恢复等边界情况

如果你的监控脚本只是快速原型,管道方式足够用;如果要写长期运行的生产级监控 agent,建议使用原生实现(Go 的 nxadm/tail 或 Python 的 pyinotify/watchdog)。