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.py或tail -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)。