fsnotify è una libreria di notifica del file system scritta in Go, che può monitorare le modifiche ai file e alle directory nel file system e notificare l'applicazione quando si verificano modifiche. Supporta il funzionamento multi-piattaforma e può essere eseguito su diversi sistemi operativi come Linux, macOS e Windows. fsnotify utilizza i meccanismi di notifica del file system dei diversi sistemi operativi, ad esempio inotify su Linux, FSEvents su macOS e ReadDirectoryChangesW su Windows.

Richiede l'uso di Go 1.17 o versioni successive; la documentazione completa può essere trovata su https://pkg.go.dev/github.com/fsnotify/fsnotify

Supporto per diversi sistemi operativi:

Backend Sistema Operativo Stato
inotify Linux Supportato
kqueue BSD, macOS Supportato
ReadDirectoryChangesW Windows Supportato
FEN illumos Supportato
fanotify Linux 5.9+ Non ancora supportato
AHAFS AIX Branch AIX; Funzionalità sperimentale a causa della mancanza di mantenitori e ambiente di test
FSEvents macOS Richiede supporto x/sys/unix
USN Journals Windows Richiede supporto x/sys/windows
Polling Tutti Non ancora supportato

Android e Solaris, che dovrebbero includere Linux e illumos, non sono ancora stati testati.

Casi d'uso

I casi d'uso di fsnotify includono, ma non sono limitati ai seguenti scenari:

  1. Sincronizzazione dei file in tempo reale: fsnotify può monitorare le modifiche nel file system in tempo reale, rendendolo adatto per implementare la sincronizzazione dei file in tempo reale. Quando si verificano modifiche nel file di origine, le modifiche possono essere immediatamente sincronizzate sul file di destinazione, garantendo la coerenza dei file.
  2. Costruzione automatizzata: fsnotify può monitorare le modifiche nel codice sorgente del progetto e nei file di dipendenza, attivando i comandi di compilazione quando si verificano modifiche, raggiungendo così la costruzione automatizzata. Questo può risparmiare tempo ed sforzi di costruzione manuale e migliorare l'efficienza dello sviluppo.
  3. Backup dei file: fsnotify può monitorare le modifiche ai file o alle directory che devono essere eseguite il backup e avviare immediatamente il backup quando si verificano modifiche. Ciò assicura la sicurezza dei dati e previene la perdita di dati dovuta alla perdita o danneggiamento dei file.
  4. Monitoraggio dei log in tempo reale: fsnotify può monitorare operazioni come la creazione, la modifica e l'eliminazione dei file di log, attivando programmi di monitoraggio dei log quando si verificano modifiche nei file di log, monitorando efficacemente le modifiche nel contenuto del log in tempo reale.
  5. Monitoraggio della sicurezza del file system: fsnotify può monitorare eventi di sicurezza nel file system, come l'accesso ai file, la modifica e l'eliminazione. Ciò consente il monitoraggio della sicurezza del file system, identificando prontamente e registrando eventi non sicuri.

Esempio di utilizzo

Un esempio di base:

package main

import (
    "log"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Crea un nuovo watcher.
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    // Inizia ad ascoltare gli eventi.
    go func() {
        for {
            select {
            case event, ok := <-watcher.Events:
                if !ok {
                    return
                }
                log.Println("Evento:", event)
                if event.Has(fsnotify.Write) {
                    log.Println("File modificato:", event.Name)
                }
            case err, ok := <-watcher.Errors:
                if !ok {
                    return
                }
                log.Println("Errore:", err)
            }
        }
    }()

    // Aggiungi il percorso che deve essere monitorato.
    err = watcher.Add("/tmp")
    if err != nil {
        log.Fatal(err)
    }

    // Blocca il main goroutine.
    <-make(chan struct{})
}

È possibile trovare ulteriori esempi in cmd/fsnotify e possono essere eseguiti utilizzando il seguente comando:

% go run ./cmd/fsnotify

È possibile trovare documentazione più dettagliata in godoc: https://pkg.go.dev/github.com/fsnotify/fsnotify

Domande frequenti

Sarà comunque monitorato se il file viene spostato in un'altra directory?

No, a meno che tu non stia monitorando la posizione in cui viene spostato.

Monitora le sottodirectory?

No, è necessario aggiungere il monitoraggio per ciascuna directory che si desidera monitorare (il monitoraggio ricorsivo è previsto: #18).

È necessario monitorare contemporaneamente i canali degli errori e degli eventi in una goroutine?

Sì. Puoi utilizzare select per leggere da entrambi i canali nella stessa goroutine (non è necessario avviare una goroutine separatamente per ciascun canale; consulta l'esempio).

Perché le notifiche non funzionano su NFS, SMB, FUSE, /proc o /sys?

fsnotify richiede il supporto del sistema operativo sottostante per funzionare. I protocolli correnti NFS e SMB non forniscono supporto a livello di rete per le notifiche dei file, e i filesystem virtuali /proc e /sys non forniscono supporto.

Ciò potrebbe essere risolto utilizzando un watcher di tipo polling (#9), ma non è stato ancora implementato.

Perché ricevo molti eventi Chmod?

Alcuni programmi possono generare un gran numero di modifiche di attributo, come Spotlight su macOS, programmi antivirus, applicazioni di backup, e altre note applicazioni. In generale, ignorare gli eventi Chmod è di solito la prassi migliore in quanto spesso sono inutili e potrebbero causare problemi.

L'indicizzazione di Spotlight su macOS potrebbe generare molti eventi (vedi #15). Una soluzione temporanea è aggiungere le tue cartelle alle Impostazioni Privacy di Spotlight fino a quando non avremo un'implementazione nativa di FSEvents (vedi #11).

Il monitoraggio dei file non funziona bene

In generale, non è consigliabile monitorare singoli file (anziché directory) perché molti programmi, specialmente gli editor, aggiorneranno atomicamente i file: scriveranno su un file temporaneo e poi lo sposteranno nella posizione di destinazione, sovrascrivendo il file originale (o una sua variante). Il monitor sul file originale è ora perso perché il file non esiste più.

Di conseguenza, un'interruzione di corrente o un arresto improvviso non produrrà un file a metà scrittura.

Monitora la directory principale e utilizza Event.Name per filtrare i file che non ti interessano. C'è un esempio in cmd/fsnotify/file.go.

Note per Piattaforme Specifiche

Linux

Quando un file viene eliminato, l'evento REMOVE non verrà emesso fino a quando tutti i descrittori di file non saranno chiusi; in questo momento verrà emesso un evento CHMOD:

fp := os.Open("file")
os.Remove("file")        // CHMOD
fp.Close()               // REMOVE

Questo è l'evento inviato da inotify, quindi non dovrebbero essere apportate troppe modifiche ad esso.

La variabile sysctl fs.inotify.max_user_watches specifica il numero massimo di watch per utente e fs.inotify.max_user_instances specifica il numero massimo di istanze inotify per utente. Ogni Watcher che crei è un' "istanza", e ogni percorso che aggiungi è un "watch".

Questi possono essere trovati anche in /proc, con i percorsi /proc/sys/fs/inotify/max_user_watches e /proc/sys/fs/inotify/max_user_instances.

Per aumentarli, puoi utilizzare sysctl o scrivere i valori nel file proc:

sysctl fs.inotify.max_user_watches=124983
sysctl fs.inotify.max_user_instances=128

Per rendere effettive le modifiche dopo un riavvio, modifica /etc/sysctl.conf o /usr/lib/sysctl.d/50-default.conf (i dettagli possono variare per ciascuna distribuzione Linux, consulta la documentazione della tua distribuzione).

fs.inotify.max_user_watches=124983
fs.inotify.max_user_instances=128

Raggiungere il limite produrrà errori "No space left on device" o "Too many open files".

kqueue (macOS, tutti i sistemi BSD)

kqueue richiede l'apertura di un descrittore di file per ciascun file osservato; quindi, se stai osservando una directory contenente cinque file, sono sei descrittori di file. Su queste piattaforme, raggiungerai più rapidamente il limite "numero massimo di file aperti" del sistema.

Puoi utilizzare le variabili sysctl kern.maxfiles e kern.maxfilesperproc per controllare il numero massimo di file aperti.