2012-04-13 85 views
28

我想解析一些日誌文件,因爲他們正在寫在Go中,但我不知道如何做到這一點,而無需重複讀取文件一次又一次改變。閱讀日誌文件,因爲他們在更新中去

我希望能夠讀取到EOF,等到下一行被寫入並再次讀取到EOF等等。它感覺有點像tail -f看起來的樣子。

回答

39

我已經寫了一個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. 
+0

關於其他答案引發的問題:當文件被移動或重命名時它的行爲如何? – Nick 2013-03-11 00:19:11

+0

@尼克 - 在答案中看到我的回答。 – 2013-03-11 20:27:56

+0

這似乎不能在Windows中正常工作,它讀取文件,但不會將其結尾。有計劃解決這個問題還是我做錯了什麼? – 2014-08-22 14:26:30

6

您必須觀察文件以進行更改(使用操作系統特定的子系統來完成此操作)或定期輪詢see whether its modification time (and size) changed。在任何一種情況下,在讀取另一塊數據後,您都會記住文件偏移量,並在檢測到更改後再讀取另一塊數據塊。

但請注意,這似乎只是在紙上很容易:在現實生活中,文件可能會被截斷,替換或重命名(因爲這是logrotate應該做的工具)。

有關此問題的更多討論,請參閱this question

+1

通知和winfsnotify目前[曝光](http://weekly.golang.org/pkg/exp/)封裝。 – Sonia 2012-04-13 15:13:48

+0

@kostix我實際上認爲你對另一個問題的回答更有幫助。但是這個答案也很有用。 – Nick 2012-04-15 01:04:45

4

我對這樣做也很感興趣,但還沒有時間來解決它。發生在我身上的一種方法是讓「尾巴」完成繁重的工作。它可能會使你的工具平臺特定,但這可能是好的。基本思路是使用「os/exec」包中的Cmd來跟蹤文件。您可以派生一個相當於「tail --retry --follow = name prog.log」的進程,然後使用Cmd對象上的Stdout reader來監聽它的Stdout。

對不起,我知道這只是一個草圖,但也許是有幫助的。

+1

而不是執行尾巴,只需從os.Stdin中讀取數據,然後讓其他數據將數據傳輸給您。這樣你就不需要弄清楚這個過程應該是什麼,別人會給你。這不僅適用於諸如「tail」之類的工具,而且適用於任何支持日誌管道輸出的平臺(例如Apache web服務器通過執行應用程序來管理數據來支持自定義記錄器)。 – pasamio 2016-01-25 16:18:06

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) 

     } 
} 

希望這有助於!

0

一個簡單的例子:

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) 
}