recwatch

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2026 License: BSD-2-Clause Imports: 14 Imported by: 5

README

recwatch

Build

Watch filesystem changes recursively.

recwatch started out as a fork of looper (by Nathan Youngman), but most of the code is now original.

Features and limitations

  • Can be used for watching filesystem changes recursively.
  • Can be used for setting up a SSE (server-sent events) server, for serving filesystem changes as events.
  • Filenames beginning with . or _ are ignored.
  • The file search is concurrent.
  • Will follow symlinks.
  • Note that some systems may have a very low ulimit which may limit how many files can be watched at the same time.

Example usage

Short example
recwatch.EventServer(pathToWatch, "*", eventAddr, eventPath, refreshDuration)
Longer example
package main

import (
    "log"
    "os"
    "os/signal"
    "path/filepath"
    "strings"
    "time"

    "github.com/xyproto/recwatch"
)

func plural(passed time.Duration) string {
    if passed.Seconds() != 1 {
        return "s"
    }
    return ""
}

// Some errors are ignored since this is just a quick example
func main() {
    eventAddr := "0.0.0.0:5555"
    eventPath := "/"
    pathToWatch := "tempdir"
    refreshDuration, err := time.ParseDuration("350ms")
    if err != nil {
        log.Fatalln(err)
    }
    now := time.Now().UTC()
    _ = os.Mkdir(pathToWatch, 0755)
    recwatch.EventServer(pathToWatch, "*", eventAddr, eventPath, refreshDuration)
    URL := "http://" + strings.Replace(eventAddr, "0.0.0.0", "localhost", 1) + eventPath
    log.Printf("Serving filesystem events for %s as SSE (server-sent events) on %s\n", pathToWatch, URL)
    tempFileName := filepath.Join(pathToWatch, "hello.txt")
    _ = os.Remove(tempFileName)
    // Set up a handler for SIGINT (ctrl-c)
    quit := false
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    go func() {
        for range c {
            // ctrl-c was pressed
            quit = true
            log.Println("ctrl-c was pressed, cleaning up")
            _ = os.Remove(tempFileName)
            _ = os.Remove(pathToWatch) // will only remove the directory if it's empty
            log.Println("done")
        }
    }()
    for {
        passed := time.Since(now)
        log.Printf("%.0f second%s passed. Visit %s to see events appear.\n", passed.Seconds(), plural(passed), URL)
        time.Sleep(1 * time.Second)
        if quit {
            break
        }
        log.Println("Creating and writing to " + tempFileName)
        data := []byte("Hi\n")
        err := os.WriteFile(tempFileName, data, 0644)
        if err != nil {
            log.Fatal(err)
        }
        time.Sleep(1 * time.Second)
        if quit {
            break
        }
        log.Println("Removing " + tempFileName)
        err = os.Remove(tempFileName)
        if err != nil {
            log.Fatal(err)
        }
        time.Sleep(1 * time.Second)
        if quit {
            break
        }
        if passed.Seconds() > 200 {
            log.Println("Time waits for no man.")
        }
    }
}

General info

Documentation

Overview

Package recwatch provides a way to watch directories recursively, using fsnotify

Index

Constants

This section is empty.

Variables

View Source
var Exists = func(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

Exists checks if the given path exists, using os.Stat

View Source
var FatalExit = func(err error) {
	log.Fatalln(err)
}

FatalExit ends the program after logging a message

View Source
var LogError = func(err error) {
	log.Println(err.Error())
}

LogError logs a message as an error, but does not end the program

View Source
var LogInfo = func(msg string) {
	log.Println(msg)
}

LogInfo logs a message as information

Functions

func CollectFileChangeEvents

func CollectFileChangeEvents(watcher *RecursiveWatcher, mut *sync.Mutex, events TimeEventMap, maxAge time.Duration)

CollectFileChangeEvents gathers filesystem events in a way that web handle functions can use

func EventHandler added in v1.2.1

func EventHandler(path, allowed, eventPath string, refreshDuration time.Duration) (http.Handler, error)

EventHandler returns an http.Handler that serves SSE file-change events for the given path. Unlike EventServer, it does not start its own http.Server — the caller can mount the handler on any mux. Event data contains absolute file paths (same as EventServer).

func EventServer

func EventServer(path, allowed, eventAddr, eventPath string, refreshDuration time.Duration)

EventServer serves events on a dedicated port. addr is the host address ([host][:port]) The filesystem events are gathered independently of that. Allowed can be "*" or a hostname and sets a header in the SSE stream.

func EventServerHandler added in v1.2.1

func EventServerHandler(path, allowed, eventPath string, refreshDuration time.Duration) (http.Handler, error)

EventServerHandler is like EventServer but returns the http.Handler instead of starting a server. It also emits relative paths. This is the recommended replacement for EventServer when embedding the SSE endpoint into an existing server.

func Flush

func Flush(w http.ResponseWriter) bool

Flush can flush the given ResponseWriter. Returns false if it wasn't an http.Flusher.

func GenFileChangeEvents

func GenFileChangeEvents(events TimeEventMap, mut *sync.Mutex, maxAge time.Duration, allowed string) http.HandlerFunc

GenFileChangeEvents creates an SSE event whenever a file in the server directory changes.

Uses the following HTTP headers:

Content-Type: text/event-stream;charset=utf-8
Cache-Control: no-cache
Connection: keep-alive
Access-Control-Allow-Origin: (custom value)

The "Access-Control-Allow-Origin" header uses the value that is passed in the "allowed" argument.

func GenRelFileChangeEvents added in v1.2.1

func GenRelFileChangeEvents(events TimeEventMap, mut *sync.Mutex, maxAge time.Duration, allowed, watchDir string) http.HandlerFunc

GenRelFileChangeEvents is like GenFileChangeEvents but emits paths relative to watchDir instead of absolute filesystem paths.

func RelEventHandler added in v1.2.1

func RelEventHandler(path, allowed, eventPath string, refreshDuration time.Duration) (http.Handler, error)

RelEventHandler is like EventHandler but emits paths relative to the watched directory instead of absolute filesystem paths.

func RemoveOldEvents

func RemoveOldEvents(events *TimeEventMap, maxAge time.Duration)

RemoveOldEvents can remove old filesystem events, after a certain duration. Needs to be called within a mutex!

func SetVerbose

func SetVerbose(enabled bool)

SetVerbose can be used to enable or disable logging of incoming events

func ShouldIgnoreFile

func ShouldIgnoreFile(name string) bool

ShouldIgnoreFile determines if a file should be ignored. File names that begin with "." or "_" are ignored by the go tool.

func Subfolders

func Subfolders(path string) (paths []string)

Subfolders returns a slice of subfolders (recursive), including the provided path

func WriteEvent

func WriteEvent(w http.ResponseWriter, id *uint64, message string, flush bool)

WriteEvent writes SSE events to the given ResponseWriter. id can be nil.

Types

type Event

type Event fsnotify.Event

Event is a custom type, so that packages that depends on recwatch does not also have to import fstnotify

func (Event) String

func (e Event) String() string

type RecursiveWatcher

type RecursiveWatcher struct {
	*fsnotify.Watcher
	Files   chan string
	Folders chan string
}

RecursiveWatcher keeps the data for watching files and directories

func NewRecursiveWatcher

func NewRecursiveWatcher(path string) (*RecursiveWatcher, error)

NewRecursiveWatcher creates a new RecursiveWatcher. Takes a path to a directory to watch.

func (*RecursiveWatcher) AddFolder

func (watcher *RecursiveWatcher) AddFolder(folder string) error

AddFolder adds a directory to watch, non-recursively

type TimeEventMap

type TimeEventMap map[time.Time]Event

TimeEventMap stores filesystem events

Directories

Path Synopsis
cmd
example command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL