You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

watcher.go 3.1KB

9 anni fa
9 anni fa
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package fs
  2. import (
  3. "os"
  4. "path/filepath"
  5. "regexp"
  6. "sync"
  7. "time"
  8. log "github.com/sirupsen/logrus"
  9. "github.com/fsnotify/fsnotify"
  10. )
  11. type watcher struct {
  12. dir string
  13. mutex sync.Mutex
  14. daemon *fsnotify.Watcher
  15. event chan *fsnotify.Event
  16. ignores []string
  17. watchList map[*regexp.Regexp]func(string)
  18. }
  19. func NewWatcher(dir string) *watcher {
  20. self := new(watcher)
  21. self.dir = dir
  22. self.event = make(chan *fsnotify.Event)
  23. self.ignores = []string{}
  24. self.watchList = make(map[*regexp.Regexp]func(string))
  25. return self
  26. }
  27. // IsWrite checks if the triggered event is fsnotify.Write|fsnotify.Create.
  28. func (self *watcher) isWrite(event *fsnotify.Event) bool {
  29. // instead of MODIFY event, editors may only send CREATE.
  30. // so we need to capture write & create.
  31. if event.Op&fsnotify.Write == fsnotify.Write ||
  32. event.Op&fsnotify.Create == fsnotify.Create {
  33. return true
  34. }
  35. return false
  36. }
  37. // IsRemove checks if the triggered event is fsnotify.Remove.
  38. func (self *watcher) isRemove(event *fsnotify.Event) bool {
  39. return event.Op&fsnotify.Remove == fsnotify.Remove
  40. }
  41. // Add appends regular expression based pattern processor into the watch list.
  42. func (self *watcher) Add(pattern *regexp.Regexp, process func(path string)) {
  43. self.mutex.Lock()
  44. defer self.mutex.Unlock()
  45. self.watchList[pattern] = process
  46. }
  47. func (self *watcher) watch() {
  48. self.daemon, _ = fsnotify.NewWatcher()
  49. if self.daemon != nil {
  50. self.daemon.Close()
  51. }
  52. // ensure we have a new daemon watcher eachtime we start watching.
  53. self.daemon, _ = fsnotify.NewWatcher()
  54. if err := self.daemon.Add(self.dir); err != nil {
  55. log.Fatalf("Failed to create fs watcher for <%s>: %v", self.dir, err)
  56. }
  57. // watch all folders under the root.
  58. filepath.Walk(self.dir, func(path string, info os.FileInfo, e error) error {
  59. if info.IsDir() {
  60. for _, ignore := range self.ignores {
  61. if info.Name() == ignore {
  62. return filepath.SkipDir
  63. }
  64. }
  65. if err := self.daemon.Add(path); err != nil {
  66. log.Fatalf("Failed create watch list for (%s): %v", info.Name(), err)
  67. }
  68. }
  69. return e
  70. })
  71. }
  72. func (self *watcher) startWatching() {
  73. self.watch()
  74. var evt *fsnotify.Event
  75. // multiple events can be triggered on a successful write
  76. // (e.g. Create followed by multiple CHMOD), just postpone
  77. // a bit to let it calm before actual processing.
  78. var delay <-chan time.Time
  79. for {
  80. select {
  81. case event := <-self.daemon.Events:
  82. // We only need "Write" event (modify | create | remove)
  83. if self.isWrite(&event) || self.isRemove(&event) {
  84. evt = &event
  85. delay = time.After(500 * time.Millisecond)
  86. }
  87. case err := <-self.daemon.Errors:
  88. log.Fatalf("Failed to watch the path %v", err)
  89. case <-delay:
  90. self.event <- evt
  91. }
  92. }
  93. }
  94. // Start watches all file changes under the root path & dispatch
  95. // to corresonding handlers (added via Add function)
  96. func (self *watcher) Start() {
  97. go self.startWatching()
  98. // listens the catched event & start processing.
  99. for event := range self.event {
  100. if event == nil {
  101. continue
  102. }
  103. // start processing the event
  104. var filename = filepath.Base(event.Name)
  105. for pattern, process := range self.watchList {
  106. if pattern.MatchString(filename) {
  107. process(event.Name)
  108. }
  109. }
  110. }
  111. }