fsnotify est une bibliothèque de notification du système de fichiers écrite en Go. Elle peut surveiller les changements apportés aux fichiers et répertoires du système de fichiers et informer l'application lorsque des changements se produisent. Elle prend en charge le fonctionnement multiplateforme et peut s'exécuter sur différents systèmes d'exploitation, tels que Linux, macOS et Windows. fsnotify utilise les mécanismes de notification du système de fichiers des différents systèmes d'exploitation, tels qu'inotify sur Linux, FSEvents sur macOS et ReadDirectoryChangesW sur Windows.

Elle nécessite l'utilisation de Go 1.17 ou de versions ultérieures ; la documentation complète peut être consultée à l'adresse https://pkg.go.dev/github.com/fsnotify/fsnotify

Prise en charge de différents systèmes d'exploitation :

Backend Système d'exploitation Statut
inotify Linux Pris en charge
kqueue BSD, macOS Pris en charge
ReadDirectoryChangesW Windows Pris en charge
FEN illumos Pris en charge
fanotify Linux 5.9+ Pas encore pris en charge
AHAFS AIX Branche AIX ; Caractéristique expérimentale en raison du manque de mainteneurs et d'environnement de test
FSEvents macOS Nécessite le support x/sys/unix
Journaux USN Windows Nécessite le support x/sys/windows
Scrutage Tous Pas encore pris en charge

Android et Solaris, qui devraient inclure Linux et illumos, n'ont pas encore été testés.

Cas d'utilisation

Les cas d'utilisation de fsnotify comprennent, mais ne se limitent pas à, les situations suivantes :

  1. Synchronisation de fichiers en temps réel : fsnotify peut surveiller les changements dans le système de fichiers en temps réel, ce qui le rend adapté à la mise en œuvre de la synchronization de fichiers en temps réel. Lorsque des changements se produisent dans le fichier source, les modifications peuvent être immédiatement synchronisées vers le fichier cible, assurant ainsi la cohérence des fichiers.
  2. Construction automatisée : fsnotify peut surveiller les modifications apportées au code source du projet et aux fichiers de dépendances, déclenchant des commandes de construction lorsque des changements se produisent, ce qui permet d'atteindre une construction automatisée. Cela peut économiser du temps et des efforts de construction manuelle et améliorer l'efficacité du développement.
  3. Sauvegarde de fichiers : fsnotify peut surveiller les changements apportés aux fichiers ou répertoires devant être sauvegardés et lancer une sauvegarde immédiate lorsque des changements se produisent. Cela garantit la sécurité des données et empêche la perte de données due à la perte ou à la détérioration des fichiers.
  4. Surveillance de logs en temps réel : fsnotify peut surveiller des opérations telles que la création, la modification et la suppression de fichiers journaux, déclenchant des programmes de surveillance de logs lorsque des changements se produisent dans les fichiers journaux, permettant ainsi de surveiller efficacement les modifications du contenu des logs en temps réel.
  5. Surveillance de la sécurité du système de fichiers : fsnotify peut surveiller les événements de sécurité dans le système de fichiers, tels que l'accès, la modification et la suppression de fichiers. Cela permet la surveillance de la sécurité du système de fichiers, identifiant rapidement et enregistrant les événements non sécurisés.

Exemple d'utilisation

Un exemple de base :

package main

import (
    "log"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Créer un nouveau watcher.
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    // Commencer à écouter les événements.
    go func() {
        for {
            select {
            case event, ok := <-watcher.Events:
                if !ok {
                    return
                }
                log.Println("Event:", event)
                if event.Has(fsnotify.Write) {
                    log.Println("Fichier modifié:", event.Name)
                }
            case err, ok := <-watcher.Errors:
                if !ok {
                    return
                }
                log.Println("Erreur:", err)
            }
        }
    }()

    // Ajouter le chemin qui doit être surveillé.
    err = watcher.Add("/tmp")
    if err != nil {
        log.Fatal(err)
    }

    // Bloquer le goroutine principale.
    <-make(chan struct{})
}

D'autres exemples peuvent être trouvés dans cmd/fsnotify et peuvent être exécutés en utilisant la commande suivante :

% go run ./cmd/fsnotify

Une documentation plus détaillée peut être trouvée dans godoc : https://pkg.go.dev/github.com/fsnotify/fsnotify

Questions Fréquemment Posées

Est-ce que le fichier continuera d'être surveillé s'il est déplacé dans un autre répertoire ?

Non, à moins que vous ne surveilliez l'emplacement où il a été déplacé.

Est-ce qu'il surveille les sous-répertoires?

Non, vous devez ajouter une surveillance pour chaque répertoire que vous souhaitez surveiller (la surveillance récursive est prévue: #18).

Est-ce que je dois surveiller à la fois les canaux d'erreur et d'événement dans une goroutine simultanément?

Oui. Vous pouvez utiliser select pour lire à partir des deux canaux dans la même goroutine (vous n'avez pas besoin de démarrer une goroutine séparément pour chaque canal; voir l'exemple).

Pourquoi les notifications ne fonctionnent-elles pas sur NFS, SMB, FUSE, /proc ou /sys?

fsnotify nécessite un support du système d'exploitation sous-jacent pour fonctionner. Les protocoles NFS et SMB actuels ne fournissent pas de support au niveau du réseau pour les notifications de fichier, et les systèmes de fichiers virtuels /proc et /sys ne fournissent pas non plus de support.

Cela pourrait être résolu en utilisant un observateur de type polling (#9), mais cela n'a pas encore été implémenté.

Pourquoi est-ce que je reçois de nombreux événements Chmod?

Certains programmes peuvent générer un grand nombre de modifications d'attributs, tels que Spotlight sur macOS, des programmes antivirus, des applications de sauvegarde, et quelques autres applications connues. En général, ignorer les événements Chmod est souvent la meilleure pratique car ils sont souvent inutiles et peuvent causer des problèmes.

L'indexation de Spotlight sur macOS peut entraîner de multiples événements (voir #15). Une solution temporaire consiste à ajouter vos dossiers aux Paramètres de confidentialité de Spotlight jusqu'à ce que nous disposions d'une implémentation native de FSEvents (voir #11).

La surveillance des fichiers ne fonctionne pas bien

Il n'est généralement pas recommandé de surveiller des fichiers individuels (plutôt que des répertoires) car de nombreux programmes, en particulier des éditeurs, mettront à jour les fichiers de manière atomique : ils écriront dans un fichier temporaire, puis le déplaceront à l'emplacement cible, écrasant le fichier original (ou une variante de celui-ci). Le suivi sur le fichier d'origine est maintenant perdu car le fichier n'existe plus.

Par conséquent, une panne de courant ou un plantage ne donnera pas un fichier partiellement écrit.

Surveillez le répertoire parent et utilisez Event.Name pour filtrer les fichiers qui ne vous intéressent pas. Il y a un exemple dans cmd/fsnotify/file.go.

Remarques pour des plates-formes spécifiques

Linux

Lorsqu'un fichier est supprimé, l'événement REMOVE ne sera pas émis tant que tous les descripteurs de fichier ne seront pas fermés ; à ce moment, un événement CHMOD sera émis :

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

C'est l'événement envoyé par inotify, donc il ne devrait pas y avoir trop de modifications apportées à cela.

La variable sysctl fs.inotify.max_user_watches spécifie le nombre maximal de surveillances par utilisateur, et fs.inotify.max_user_instances spécifie le nombre maximal d'instances inotify par utilisateur. Chaque Watcher que vous créez est une "instance", et chaque chemin que vous ajoutez est une "surveillance".

Ces valeurs peuvent également être trouvées dans /proc, avec les chemins /proc/sys/fs/inotify/max_user_watches and /proc/sys/fs/inotify/max_user_instances.

Pour les augmenter, vous pouvez utiliser sysctl ou écrire les valeurs dans le fichier proc :

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

Pour que les modifications prennent effet après un redémarrage, éditez /etc/sysctl.conf ou /usr/lib/sysctl.d/50-default.conf (les détails peuvent varier pour chaque distribution Linux, veuillez vous référer à la documentation de votre distribution) :

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

Atteindre la limite entraînera des erreurs "Aucun espace disponible sur le dispositif" ou "Trop de fichiers ouverts".

kqueue (macOS, tous les systèmes BSD)

kqueue nécessite l'ouverture d'un descripteur de fichier pour chaque fichier observé ; par conséquent, si vous observez un répertoire contenant cinq fichiers, cela fait six descripteurs de fichier. Sur ces plates-formes, vous atteindrez plus rapidement la limite du "nombre maximal de fichiers ouverts" du système.

Vous pouvez utiliser les variables sysctl kern.maxfiles and kern.maxfilesperproc pour contrôler le nombre maximal de fichiers ouverts.