func watch(base string, ignorePatterns []*regexp.Regexp, watcher *fsnotify.Watcher) error { if base == "" { base = "." } if debug { log.Printf("walks watched directories '%s'", base) } // register watched directories. err := filepath.Walk(base, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() { // watche only directries return nil } path = normalizePath(path) for _, pattern := range ignorePatterns { if !strings.HasPrefix(path, "/") { // add "/" to the path if ok := pattern.MatchString("/" + path); ok { return nil } } else { if ok := pattern.MatchString(path); ok { return nil } } } err = watcher.Add(path) if err != nil { return err } if debug { log.Printf("added watched dir '%s'", path) } return nil }) if err != nil { return err } return nil }
func startWatching(watcher *fsnotify.Watcher, state *core.BuildState, labels []core.BuildLabel, files cmap.ConcurrentMap) { // Deduplicate seen targets & sources. targets := map[*core.BuildTarget]struct{}{} dirs := map[string]struct{}{} var startWatch func(*core.BuildTarget) startWatch = func(target *core.BuildTarget) { if _, present := targets[target]; present { return } targets[target] = struct{}{} for _, source := range target.AllSources() { if source.Label() == nil { for _, src := range source.Paths(state.Graph) { files.Set(src, struct{}{}) if info, err := os.Stat(src); err == nil && !info.IsDir() { src = path.Dir(src) } if _, present := dirs[src]; !present { log.Notice("Adding watch on %s", src) dirs[src] = struct{}{} if err := watcher.Add(src); err != nil { log.Error("Failed to add watch on %s: %s", src, err) } } } } } for _, dep := range target.Dependencies() { startWatch(dep) } for _, subinclude := range state.Graph.PackageOrDie(target.Label.PackageName).Subincludes { startWatch(state.Graph.TargetOrDie(subinclude)) } } for _, label := range labels { startWatch(state.Graph.TargetOrDie(label)) } // Drop a message here so they know when it's actually ready to go. fmt.Println("And now my watch begins...") }
// AddRecursiveWatch handles adding watches recursively for the path provided // and its subdirectories. If a non-directory is specified, this call is a no-op. // Recursive logic from https://github.com/bronze1man/kmg/blob/master/fsnotify/Watcher.go func AddRecursiveWatch(watcher *fsnotify.Watcher, path string) error { file, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return nil } return fmt.Errorf("error introspecting path %s: %v", path, err) } if !file.IsDir() { return nil } folders, err := getSubFolders(path) for _, v := range folders { glog.V(5).Infof("adding watch on path %s", v) err = watcher.Add(v) if err != nil { // "no space left on device" issues are usually resolved via // $ sudo sysctl fs.inotify.max_user_watches=65536 return fmt.Errorf("error adding watcher for path %s: %v", v, err) } } return nil }
// ReadLogs read the container log and redirect into stdout and stderr. func ReadLogs(path string, apiOpts *api.PodLogOptions, stdout, stderr io.Writer) error { f, err := os.Open(path) if err != nil { return fmt.Errorf("failed to open log file %q: %v", path, err) } defer f.Close() // Convert api.PodLogOptions into internal log options. opts := newLogOptions(apiOpts, time.Now()) // Search start point based on tail line. start, err := tail(f, opts.tail) if err != nil { return fmt.Errorf("failed to tail %d lines of log file %q: %v", opts.tail, path, err) } if _, err := f.Seek(start, os.SEEK_SET); err != nil { return fmt.Errorf("failed to seek %d in log file %q: %v", start, path, err) } // Start parsing the logs. r := bufio.NewReader(f) // Do not create watcher here because it is not needed if `Follow` is false. var watcher *fsnotify.Watcher var parse parseFunc writer := newLogWriter(stdout, stderr, opts) msg := &logMessage{} for { l, err := r.ReadBytes(eol[0]) if err != nil { if err != io.EOF { // This is an real error return fmt.Errorf("failed to read log file %q: %v", path, err) } if !opts.follow { // Return directly when reading to the end if not follow. if len(l) > 0 { glog.Warningf("Incomplete line in log file %q: %q", path, l) } glog.V(2).Infof("Finish parsing log file %q", path) return nil } // Reset seek so that if this is an incomplete line, // it will be read again. if _, err := f.Seek(-int64(len(l)), os.SEEK_CUR); err != nil { return fmt.Errorf("failed to reset seek in log file %q: %v", path, err) } if watcher == nil { // Intialize the watcher if it has not been initialized yet. if watcher, err = fsnotify.NewWatcher(); err != nil { return fmt.Errorf("failed to create fsnotify watcher: %v", err) } defer watcher.Close() if err := watcher.Add(f.Name()); err != nil { return fmt.Errorf("failed to watch file %q: %v", f.Name(), err) } } // Wait until the next log change. if err := waitLogs(watcher); err != nil { return fmt.Errorf("failed to wait logs for log file %q: %v", path, err) } continue } if parse == nil { // Intialize the log parsing function. parse, err = getParseFunc(l) if err != nil { return fmt.Errorf("failed to get parse function: %v", err) } } // Parse the log line. msg.reset() if err := parse(l, msg); err != nil { glog.Errorf("Failed with err %v when parsing log for log file %q: %q", err, path, l) continue } // Write the log line into the stream. if err := writer.write(msg); err != nil { if err == errMaximumWrite { glog.V(2).Infof("Finish parsing log file %q, hit bytes limit %d(bytes)", path, opts.bytes) return nil } glog.Errorf("Failed with err %v when writing log for log file %q: %+v", err, path, msg) } } }