中断当前尾部并启动新的尾部

时间:2016-12-20 13:12:54

标签: go concurrency

所以我在golang中创建了一些日志分析器,我需要的是新创建文件的实时tail -f。

我正在使用tail包和fsnotify包,但我不太熟悉其中的频道和例程,所以我需要一些帮助。

目前的计划如下:

package main

import(
    "fmt"
    "github.com/hpcloud/tail"
    "strings"
    "log"
    "net/smtp"
    "time"
    "github.com/fsnotify/fsnotify"
)

//this function needs to monitor for new files in directory
func newFileCheck() (newFilename chan string, err error) {
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        return
    }

    err = watcher.Add("myDir")
    if err != nil {
        return
    }

    newFilename = make(chan string)

    // Process events
    go func() {
        for {
            select {
            case ev := <-watcher.Events:
                log.Println("event:", ev)
                newFilename <- ev.Name // Relative path to the file
                //t.Stop() //if I pass reference to t THIS IS NOT HAPPENING ?
            case err := <-watcher.Errors:
                log.Println("error:", err)
            }
        }
    }()

    return
}

func main() {
    newFileName = "mylog_0000.log.txt"

    fmt.Println("Processing log: ",newFileName)

    newFilenameChan, err := newFileCheck()
    if err != nil {
        fmt.Println("ERR: ",err)
    }

    t := tailLog(newFileName)

    go func() {
        for {
            select {
                case name := <-newFilenameChan:
                    fmt.Println("New file created: ",name) //this will be printed only once and then on new events nothing is printed ?

                //NONE of the lines abowe doesn't work
                t.Stop()
                t.Dead()
                t.Done()

                t = tailLog(name)
            }
        }
    }()
}

func tailLog(fileName string) *tail.Tail{
    var count = 0
    // close the old one and read new file
    t, err := tail.TailFile("/mydir/"+fileName, tail.Config{Follow: true, ReOpen: true})

    for line := range t.Lines {
        //fmt.Println("Line is:", line.Text)

        //check do we have what we need
        if strings.Contains(strings.ToLower(line.Text), "mfc"){

            count++
            //do other stuff
        }
    }

    fmt.Println(err)

    return t
}

所以我无法弄清楚为什么newFileCheck函数只会在第一时间打印事件,而且我也无法弄清楚当新事件发生时如何取消活动尾部,然后在该事件之后再次启动尾部?

2 个答案:

答案 0 :(得分:0)

考虑一下:

  

当函数main返回时,程序退出。它不等待   其他(非主要)goroutines完成。

看看你的代码块:

func main() {
    newFileName = "mylog_0000.log.txt"

    fmt.Println("Processing log: ",newFileName)

    newFilenameChan, err := newFileCheck()
    if err != nil {
        fmt.Println("ERR: ",err)
    }

    t := tailLog(newFileName)

    go func() {
        for {
            select {
                case name := <-newFilenameChan:
                    fmt.Println("New file created: ",name) //this will be printed only once and then on new events nothing is printed ?

                //NONE of the lines abowe doesn't work
                t.Stop()
                t.Dead()
                t.Done()

                t = tailLog(name)
            }
        }
    }()
}

要优雅地处理这项工作,你应该在Golang中了解更多关于并发性的知识。如果您对执行/完成任务的顺序感兴趣,请使用通道来控制goroutines,例如停止/启动/ etc和WaitGroup。当无法控制你的程序流程时,goroutines可以过自己的生活,这是不好的做法。

答案 1 :(得分:0)

如果您对此包装https://github.com/hpcloud/tail

打开文件后,您需要创建一个goroutine,然后需要检查一个新文件。

我将其用于检查SoftetherVPN服务器的新日志文件:

package main

import (
    "fmt"
    "github.com/hpcloud/tail"
    "log"
    "time"
)

func CheckFileName(filename string, f chan<- string) {
    for {
        newFilename := fmt.Sprintf("%v", time.Now().Format("vpn_20060102.log"))

        // We have a new filename, that send it into channel
        if newFilename != filename {
            log.Println("A new logfile", newFilename)
            f <- newFilename
            return
        }
    }
}

func main() {

    filenameChan := make(chan string)

    for {
        filename = fmt.Sprintf("%v", time.Now().Format("vpn_20060102.log"))

        t, err := tail.TailFile(filename, tail.Config{Follow: true, ReOpen: true, MustExist: true})
        if err != nil {
            log.Println("No log file", filename, ". Waiting for 10 minute")
            time.Sleep(time.Minute * 10)
            continue
        }

        // Send goroutine for checking filename in loop
        go CheckFileName(filename, filenameChan)

        // Send goroutine that stops lines read in range
        go func() {
            for {
                select {
                case name := <-filenameChan:
                    log.Println("Received a new name for log:", name)
                    t.Stop()
                    return
                }
            }
        }()

        if err != nil {
            log.Fatalln(err)
        }
        for line := range t.Lines {
            fmt.Println(line)
        }
    }
}

所以,这对我有用