了解从频道读取的行为
Understanding behavior of reading from channel
我正在尝试编写一个使用 inotify 监视文件的程序,如果文件被删除,请删除监视程序并设置新的监视程序。我尝试过的代码是
func main() {
fsNotifyChan := make(chan fsnotify.Event)
inotify.CreateWatcher() // code included below
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for i := range fsNotifyChan {
time.Sleep(time.Second * 5)
fmt.Println(i)
inotify.CreateWatcher()
inotify.SetNewWatcher(i.Name, fsNotifyChan)
}
}()
for k := range parsedConf{
go inotify.SetNewWatcher(k, fsNotifyChan)
}
wg.Wait()
}
其中 k 是映射,键是 2 个文件的路径 /var/log/syslog
和 /var/log/auth.log
例如。
我用来创建inotify watcher的函数是
package inotify
var Watcher *fsnotify.Watcher
var err error
func CreateWatcher () {
Watcher, err = fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
}
func SetNewWatcher(filepath string, c chan fsnotify.Event) {
log.Infoln("Setting Watcher for ", filepath)
defer Watcher.Close()
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for {
select {
case event := <-Watcher.Events:
log.Debugln("event:", event)
if event.Op&fsnotify.Rename == fsnotify.Rename {
log.Infoln(event)
removeWatcher(filepath)
c <- event
wg.Done()
runtime.Goexit()
} else if event.Op&fsnotify.Remove == fsnotify.Remove {
log.Infoln(event)
removeWatcher(filepath)
c <- event
wg.Done()
runtime.Goexit()
}
case err := <-Watcher.Errors:
log.Errorln("error:", err)
removeWatcher(filepath)
wg.Done()
runtime.Goexit()
}
}
wg.Done()
}()
err = Watcher.Add(filepath)
if err != nil {
log.Fatal(err)
}
wg.Wait()
}
func removeWatcher(filename string) {
err := Watcher.Remove(filename)
log.Debugln("Removed watcher for", filename)
if err != nil {
log.Errorln(err)
}
}
我遇到的问题是当我开始 运行 程序时,
第一个输出:
iNotifier.go:18: INFO: Setting Watcher for /var/log/auth.log
iNotifier.go:18: INFO: Setting Watcher for /var/log/syslog
然后在像 echo hi | sudo tee -a /var/log/syslog
这样的 sudo 命令之后
我可以看到
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
现在完全没问题了。
现在,如果我尝试移动系统日志并将其放回
➜ bin sudo mv /var/log/syslog /var/log/syslog.bak
➜ bin sudo mv /var/log/syslog.bak /var/log/syslog
或删除文件本身并触摸一个新文件。
输出看起来像
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": RENAME
iNotifier.go:29: INFO: "/var/log/syslog": RENAME
iNotifier.go:63: DEBUG: Removed watcher for /var/log/syslog
iNotifier.go:43: ERROR: error: <nil>
iNotifier.go:63: DEBUG: Removed watcher for /var/log/auth.log
iNotifier.go:65: ERROR: bad file descriptor
"/var/log/syslog": RENAME
iNotifier.go:18: INFO: Setting Watcher for /var/log/syslog
其中iNotifier.go:65: ERROR: bad file descriptor
可能是因为文件已经移动,然后goroutine会退出runtime.Goexit()
现在,如果我执行相同的 sudo 命令 echo hi | sudo tee -a /var/log/syslog
,
我只能从 syslog 文件中看到一个 inotify 输出,而从 authlog 文件中看不到,尽管那里写了一些东西。
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
如果我再次移动文件并再次将其移回原位,我将不再收到任何通知。
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": RENAME
iNotifier.go:29: INFO: "/var/log/syslog": RENAME
iNotifier.go:63: DEBUG: Removed watcher for /var/log/syslog
这是最后的输出。任何更多的文件操作,我都看不到任何输出。我知道这可能是关于我如何使用通道应该使用的方式的逻辑错误。我没有明确关闭频道,而是在进一步的迭代中再次传递它。有人可以帮助我了解我在这里做错了什么吗?
尝试了几个选项后,我选择了另一个 notify 库。显然这个库没有选项可以根据路径名单独关闭观察者,但只能通过关闭通道来实现。所以我最终为每个需要观看的文件创建了一个频道,并创建了一个将文件与频道相关联的地图,并在需要时关闭与文件相关的频道并重新打开。代码片段是
var fileMapChan map[string]chan notify.EventInfo
func main() {
fileMapChan = make(map[string]chan notify.EventInfo)
commonNotificationChan := make(chan string, 2048)
for k := range parsedConf {
c := make(chan notify.EventInfo, 2048)
fileMapChan[k] = c
}
wg := new(sync.WaitGroup)
//Create new watcher for every file
for k, v := range fileMapChan {
wg.Add(2)
poller.SetNewNotifier(k, v)
go poller.ReadChanAndFilter(v, commonNotificationChan, wg)
go poller.FileStat(k, commonNotificationChan, wg)
}
==============snip ✂ snip=============
func SetNewNotifier(filepath string, c chan notify.EventInfo) error{
log.Infoln("Setting new notifier to", filepath)
if err := notify.Watch(filepath, c, notify.All); err != nil {
log.Errorln(err)
return err
}
return nil
}
func ReadChanAndFilter(r chan notify.EventInfo, w chan<- string, wg *sync.WaitGroup) {
for i := range r {
log.Debugln(i.Event(), "on", i.Path())
if i.Event()¬ify.Rename == notify.Rename {
log.Infoln(i.Path(), "renamed")
w <- i.Path()
close(r)
notify.Stop(r)
runtime.Goexit()
}
}
defer wg.Done()
}
我不知道这是否是最好的方法,但这对我有用。每当移动文件时,它都会重新添加观察程序。
谢谢。
我正在尝试编写一个使用 inotify 监视文件的程序,如果文件被删除,请删除监视程序并设置新的监视程序。我尝试过的代码是
func main() {
fsNotifyChan := make(chan fsnotify.Event)
inotify.CreateWatcher() // code included below
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for i := range fsNotifyChan {
time.Sleep(time.Second * 5)
fmt.Println(i)
inotify.CreateWatcher()
inotify.SetNewWatcher(i.Name, fsNotifyChan)
}
}()
for k := range parsedConf{
go inotify.SetNewWatcher(k, fsNotifyChan)
}
wg.Wait()
}
其中 k 是映射,键是 2 个文件的路径 /var/log/syslog
和 /var/log/auth.log
例如。
我用来创建inotify watcher的函数是
package inotify
var Watcher *fsnotify.Watcher
var err error
func CreateWatcher () {
Watcher, err = fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
}
func SetNewWatcher(filepath string, c chan fsnotify.Event) {
log.Infoln("Setting Watcher for ", filepath)
defer Watcher.Close()
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for {
select {
case event := <-Watcher.Events:
log.Debugln("event:", event)
if event.Op&fsnotify.Rename == fsnotify.Rename {
log.Infoln(event)
removeWatcher(filepath)
c <- event
wg.Done()
runtime.Goexit()
} else if event.Op&fsnotify.Remove == fsnotify.Remove {
log.Infoln(event)
removeWatcher(filepath)
c <- event
wg.Done()
runtime.Goexit()
}
case err := <-Watcher.Errors:
log.Errorln("error:", err)
removeWatcher(filepath)
wg.Done()
runtime.Goexit()
}
}
wg.Done()
}()
err = Watcher.Add(filepath)
if err != nil {
log.Fatal(err)
}
wg.Wait()
}
func removeWatcher(filename string) {
err := Watcher.Remove(filename)
log.Debugln("Removed watcher for", filename)
if err != nil {
log.Errorln(err)
}
}
我遇到的问题是当我开始 运行 程序时,
第一个输出:
iNotifier.go:18: INFO: Setting Watcher for /var/log/auth.log
iNotifier.go:18: INFO: Setting Watcher for /var/log/syslog
然后在像 echo hi | sudo tee -a /var/log/syslog
这样的 sudo 命令之后
我可以看到
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
现在完全没问题了。
现在,如果我尝试移动系统日志并将其放回
➜ bin sudo mv /var/log/syslog /var/log/syslog.bak
➜ bin sudo mv /var/log/syslog.bak /var/log/syslog
或删除文件本身并触摸一个新文件。
输出看起来像
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/auth.log": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": RENAME
iNotifier.go:29: INFO: "/var/log/syslog": RENAME
iNotifier.go:63: DEBUG: Removed watcher for /var/log/syslog
iNotifier.go:43: ERROR: error: <nil>
iNotifier.go:63: DEBUG: Removed watcher for /var/log/auth.log
iNotifier.go:65: ERROR: bad file descriptor
"/var/log/syslog": RENAME
iNotifier.go:18: INFO: Setting Watcher for /var/log/syslog
其中iNotifier.go:65: ERROR: bad file descriptor
可能是因为文件已经移动,然后goroutine会退出runtime.Goexit()
现在,如果我执行相同的 sudo 命令 echo hi | sudo tee -a /var/log/syslog
,
我只能从 syslog 文件中看到一个 inotify 输出,而从 authlog 文件中看不到,尽管那里写了一些东西。
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
如果我再次移动文件并再次将其移回原位,我将不再收到任何通知。
iNotifier.go:27: DEBUG: event: "/var/log/syslog": WRITE
iNotifier.go:27: DEBUG: event: "/var/log/syslog": RENAME
iNotifier.go:29: INFO: "/var/log/syslog": RENAME
iNotifier.go:63: DEBUG: Removed watcher for /var/log/syslog
这是最后的输出。任何更多的文件操作,我都看不到任何输出。我知道这可能是关于我如何使用通道应该使用的方式的逻辑错误。我没有明确关闭频道,而是在进一步的迭代中再次传递它。有人可以帮助我了解我在这里做错了什么吗?
尝试了几个选项后,我选择了另一个 notify 库。显然这个库没有选项可以根据路径名单独关闭观察者,但只能通过关闭通道来实现。所以我最终为每个需要观看的文件创建了一个频道,并创建了一个将文件与频道相关联的地图,并在需要时关闭与文件相关的频道并重新打开。代码片段是
var fileMapChan map[string]chan notify.EventInfo
func main() {
fileMapChan = make(map[string]chan notify.EventInfo)
commonNotificationChan := make(chan string, 2048)
for k := range parsedConf {
c := make(chan notify.EventInfo, 2048)
fileMapChan[k] = c
}
wg := new(sync.WaitGroup)
//Create new watcher for every file
for k, v := range fileMapChan {
wg.Add(2)
poller.SetNewNotifier(k, v)
go poller.ReadChanAndFilter(v, commonNotificationChan, wg)
go poller.FileStat(k, commonNotificationChan, wg)
}
==============snip ✂ snip=============
func SetNewNotifier(filepath string, c chan notify.EventInfo) error{
log.Infoln("Setting new notifier to", filepath)
if err := notify.Watch(filepath, c, notify.All); err != nil {
log.Errorln(err)
return err
}
return nil
}
func ReadChanAndFilter(r chan notify.EventInfo, w chan<- string, wg *sync.WaitGroup) {
for i := range r {
log.Debugln(i.Event(), "on", i.Path())
if i.Event()¬ify.Rename == notify.Rename {
log.Infoln(i.Path(), "renamed")
w <- i.Path()
close(r)
notify.Stop(r)
runtime.Goexit()
}
}
defer wg.Done()
}
我不知道这是否是最好的方法,但这对我有用。每当移动文件时,它都会重新添加观察程序。
谢谢。