读取Go中更新的日志文件

问题描述 投票:36回答:5

我正在尝试解析一些日志文件,因为它们是在Go中编写的,但是我不确定如何在不检查更改的情况下一次又一次地重新读取文件的情况下完成此操作。

我希望能够读到EOF,等到下一行写入并再次读到EOF等等。感觉有点像tail -f看起来。

go
5个回答
45
投票

我写了一个Go包 - github.com/ActiveState/tail--正是这样做的。

t, err := tail.TailFile("/var/log/nginx.log", tail.Config{Follow: true})
for line := range t.Lines {
    fmt.Println(line.Text)
}

...

引用kostix的答案:

在现实生活中,文件可能会被截断,替换或重命名(因为这就像logrotate这样的工具应该这样做)。

如果文件被截断,它将自动重新打开。为了支持重新打开重命名的文件(由于logrotate等),你可以设置Config.ReOpen,即:

t, err := tail.TailFile("/var/log/nginx.log", tail.Config{
    Follow: true,
    ReOpen: true})
for line := range t.Lines {
    fmt.Println(line.Text)
}

Config.ReOpen类似于tail -F(首都F):

 -F      The -F option implies the -f option, but tail will also check to see if the file being followed has been
         renamed or rotated.  The file is closed and reopened when tail detects that the filename being read from
         has a new inode number.  The -F option is ignored if reading from standard input rather than a file.

6
投票

您必须观察文件的更改(使用特定于操作系统的子系统来完成此操作)或定期将其轮询到see whether its modification time (and size) changed。在任何一种情况下,在读取另一个数据块之后,您会在检测到更改后记住文件偏移量和restore it before reading另一个块。

但请注意,这似乎只是在纸上很容易:在现实生活中,文件可能会被截断,替换或重命名(因为这就像logrotate这样的工具应该这样做)。

有关此问题的更多讨论,请参阅this question


4
投票

我也有兴趣这样做,但还没有(还)有时间解决它。我遇到的一种方法是让“尾巴”完成繁重的工作。它可能会使您的工具特定于平台,但这可能没问题。基本的想法是使用“os / exec”包中的Cmd来跟踪文件。您可以分叉一个相当于“tail --retry --follow = name prog.log”的进程,然后使用Cmd对象上的Stdout reader读取它的Stdout。

对不起,我知道这只是一个草图,但也许它很有帮助。


1
投票

一个简单的例子:

package main

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

func tail(filename string, out io.Writer) {
    f, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    defer f.Close()
    r := bufio.NewReader(f)
    info, err := f.Stat()
    if err != nil {
        panic(err)
    }
    oldSize := info.Size()
    for {
        for line, prefix, err := r.ReadLine(); err != io.EOF; line, prefix, err = r.ReadLine() {
            if prefix {
                fmt.Fprint(out, string(line))
            } else {
                fmt.Fprintln(out, string(line))
            }
        }
        pos, err := f.Seek(0, io.SeekCurrent)
        if err != nil {
            panic(err)
        }
        for {
            time.Sleep(time.Second)
            newinfo, err := f.Stat()
            if err != nil {
                panic(err)
            }
            newSize := newinfo.Size()
            if newSize != oldSize {
                if newSize < oldSize {
                    f.Seek(0, 0)
                } else {
                    f.Seek(pos, io.SeekStart)
                }
                r = bufio.NewReader(f)
                oldSize = newSize
                break
            }
        }
    }
}

func main() {
    tail("x.txt", os.Stdout)
}

0
投票

有很多方法可以做到这一点。在现代基于POSIX的操作系统中,可以使用inotify接口来执行此操作。

可以使用这个包:https://github.com/fsnotify/fsnotify

示例代码:

watcher, err := fsnotify.NewWatcher()
if err != nil {
    log.Fatal(err)
}

done := make(chan bool)

err = watcher.Add(fileName)
if err != nil {
    log.Fatal(err)
}
for {
    select {
    case event := <-watcher.Events:
        if event.Op&fsnotify.Write == fsnotify.Write {
            log.Println("modified file:", event.Name)

        }
}

希望这可以帮助!

© www.soinside.com 2019 - 2024. All rights reserved.