github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/config/watcher.go (about)

     1  package config
     2  
     3  import (
     4  	"path/filepath"
     5  
     6  	"github.com/fsnotify/fsnotify"
     7  	"github.com/pkg/errors"
     8  
     9  	"github.com/masterhung0112/hk_server/v5/shared/mlog"
    10  )
    11  
    12  // watcher monitors a file for changes
    13  type watcher struct {
    14  	fsWatcher *fsnotify.Watcher
    15  	close     chan struct{}
    16  	closed    chan struct{}
    17  }
    18  
    19  // newWatcher creates a new instance of watcher to monitor for file changes.
    20  func newWatcher(path string, callback func()) (w *watcher, err error) {
    21  	fsWatcher, err := fsnotify.NewWatcher()
    22  	if err != nil {
    23  		return nil, errors.Wrapf(err, "failed to create fsnotify watcher for %s", path)
    24  	}
    25  
    26  	path = filepath.Clean(path)
    27  
    28  	// Watch the entire containing directory.
    29  	configDir, _ := filepath.Split(path)
    30  	if err := fsWatcher.Add(configDir); err != nil {
    31  		if closeErr := fsWatcher.Close(); closeErr != nil {
    32  			mlog.Error("failed to stop fsnotify watcher for %s", mlog.String("path", path), mlog.Err(closeErr))
    33  		}
    34  		return nil, errors.Wrapf(err, "failed to watch directory %s", configDir)
    35  	}
    36  
    37  	w = &watcher{
    38  		fsWatcher: fsWatcher,
    39  		close:     make(chan struct{}),
    40  		closed:    make(chan struct{}),
    41  	}
    42  
    43  	go func() {
    44  		defer close(w.closed)
    45  		defer func() {
    46  			if err := fsWatcher.Close(); err != nil {
    47  				mlog.Error("failed to stop fsnotify watcher for %s", mlog.String("path", path))
    48  			}
    49  		}()
    50  
    51  		for {
    52  			select {
    53  			case event := <-fsWatcher.Events:
    54  				// We only care about the given file.
    55  				if filepath.Clean(event.Name) == path {
    56  					if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
    57  						mlog.Info("Config file watcher detected a change", mlog.String("path", path))
    58  						go callback()
    59  					}
    60  				}
    61  			case err := <-fsWatcher.Errors:
    62  				mlog.Error("Failed while watching config file", mlog.String("path", path), mlog.Err(err))
    63  			case <-w.close:
    64  				return
    65  			}
    66  		}
    67  	}()
    68  
    69  	return w, nil
    70  }
    71  
    72  func (w *watcher) Close() error {
    73  	close(w.close)
    74  	<-w.closed
    75  
    76  	return nil
    77  }