O fsnotify é uma biblioteca de notificação do sistema de arquivos escrita em Go, que pode monitorar alterações em arquivos e diretórios no sistema de arquivos e notificar a aplicação quando ocorrem mudanças. Ele oferece suporte à operação multiplataforma e pode ser executado em diferentes sistemas operacionais, como Linux, macOS e Windows. O fsnotify utiliza os mecanismos de notificação do sistema de arquivos de diferentes sistemas operacionais, como inotify no Linux, FSEvents no macOS e ReadDirectoryChangesW no Windows.

Requer o uso do Go 1.17 ou versões posteriores; a documentação completa pode ser encontrada em https://pkg.go.dev/github.com/fsnotify/fsnotify

Suporte para diferentes sistemas operacionais:

Backend Sistema Operacional Status
inotify Linux Suportado
kqueue BSD, macOS Suportado
ReadDirectoryChangesW Windows Suportado
FEN illumos Suportado
fanotify Linux 5.9+ Ainda não suportado
AHAFS AIX Branch AIX; Funcionalidade experimental devido à falta de mantenedores e ambiente de teste
FSEvents macOS Requer suporte x/sys/unix
Jornais USN Windows Requer suporte x/sys/windows
Polling Todos Ainda não suportado

Android e Solaris, que devem incluir Linux e illumos, ainda não foram testados.

Casos de Uso

Os casos de uso do fsnotify incluem, mas não se limitam a, situações como:

  1. Sincronização de arquivos em tempo real: o fsnotify pode monitorar alterações no sistema de arquivos em tempo real, sendo adequado para implementar a sincronização de arquivos em tempo real. Quando ocorrem alterações no arquivo de origem, as alterações podem ser imediatamente sincronizadas com o arquivo de destino, garantindo a consistência dos arquivos.
  2. Compilação automatizada: o fsnotify pode monitorar alterações no código-fonte do projeto e nos arquivos de dependência, acionando comandos de compilação quando ocorrerem alterações, alcançando assim a compilação automatizada. Isso pode economizar tempo e esforço da compilação manual e melhorar a eficiência do desenvolvimento.
  3. Backup de arquivos: o fsnotify pode monitorar alterações em arquivos ou diretórios que precisam ser copiados e iniciar o backup imediato quando ocorrerem alterações. Isso garante a segurança dos dados e evita a perda de dados devido à perda ou dano de arquivos.
  4. Monitoramento de log em tempo real: o fsnotify pode monitorar operações como criação, modificação e exclusão de arquivos de log, acionando programas de monitoramento de log quando ocorrem alterações nos arquivos de log, monitorando efetivamente as alterações no conteúdo do log em tempo real.
  5. Monitoramento de segurança do sistema de arquivos: o fsnotify pode monitorar eventos de segurança no sistema de arquivos, como acesso, modificação e exclusão de arquivos. Isso permite o monitoramento de segurança do sistema de arquivos, identificando e registrando prontamente eventos inseguros.

Exemplo de Uso

Um exemplo básico:

package main

import (
    "log"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Cria um novo observador.
    observador, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer observador.Close()

    // Começa a ouvir eventos.
    go func() {
        for {
            select {
            case evento, ok := <-observador.Events:
                if !ok {
                    return
                }
                log.Println("Evento:", evento)
                if evento.Has(fsnotify.Write) {
                    log.Println("Arquivo Modificado:", evento.Name)
                }
            case err, ok := <-observador.Errors:
                if !ok {
                    return
                }
                log.Println("Erro:", err)
            }
        }
    }()

    // Adiciona o caminho que precisa ser observado.
    err = observador.Add("/tmp")
    if err != nil {
        log.Fatal(err)
    }

    // Bloqueia a gorrotina principal.
    <-make(chan struct{})
}

Mais exemplos podem ser encontrados em cmd/fsnotify e podem ser executados usando o seguinte comando:

% go run ./cmd/fsnotify

Documentação mais detalhada pode ser encontrada em godoc: https://pkg.go.dev/github.com/fsnotify/fsnotify

Perguntas Frequentes

Ele continuará sendo monitorado se o arquivo for movido para outro diretório?

Não, a menos que você esteja monitorando a localização para onde ele foi movido.

Ele monitora subdiretórios?

Não, você deve adicionar monitoramento para cada diretório que deseja monitorar (o monitoramento recursivo está planejado: #18).

Preciso monitorar simultaneamente os canais de erro e evento em uma goroutine?

Sim. Você pode usar select para ler de ambos os canais na mesma goroutine (você não precisa iniciar uma goroutine separadamente para cada canal; veja o exemplo).

Por que as notificações não funcionam em NFS, SMB, FUSE, /proc, ou /sys?

O fsnotify requer suporte do sistema operacional subjacente para funcionar. Os protocolos atuais NFS e SMB não oferecem suporte em nível de rede para notificações de arquivos, e os sistemas de arquivos virtuais /proc e /sys também não oferecem suporte.

Isso poderia ser corrigido usando um observador de polling (#9), mas ainda não foi implementado.

Por que estou recebendo muitos eventos de Chmod?

Alguns programas podem gerar um grande número de alterações de atributos, como o Spotlight no macOS, programas antivírus, aplicativos de backup e alguns outros aplicativos conhecidos. Em geral, ignorar eventos de Chmod geralmente é a melhor prática, pois muitas vezes são inúteis e podem causar problemas.

A indexação do Spotlight no macOS pode causar múltiplos eventos (veja #15). Uma solução temporária é adicionar suas pastas às Configurações de Privacidade do Spotlight até que tenhamos uma implementação nativa do FSEvents (veja #11).

Observar arquivos não funciona bem

Em geral, não é recomendado observar arquivos individuais (em vez de diretórios) porque muitos programas, especialmente editores, atualizarão atomicamente os arquivos: eles gravarão em um arquivo temporário e depois o moverão para o local de destino, sobrescrevendo o arquivo original (ou algum de seus variantes). O monitoramento no arquivo original agora está perdido porque o arquivo não existe mais.

Como resultado, uma falha de energia ou uma falha não resultará em um arquivo escrito pela metade.

Observe o diretório pai e use Event.Name para filtrar os arquivos que não lhe interessam. Há um exemplo em cmd/fsnotify/file.go.

Notas para Plataformas Específicas

Linux

Quando um arquivo é excluído, o evento REMOVE não será emitido até que todos os descritores de arquivo sejam fechados; neste momento, um evento CHMOD será emitido:

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

Este é o evento enviado pelo inotify, portanto, não deve haver muitas alterações feitas nele.

A variável de sysctl fs.inotify.max_user_watches especifica o número máximo de observações por usuário, e fs.inotify.max_user_instances especifica a quantidade máxima de instâncias inotify por usuário. Cada Watcher que você cria é uma "instância" e cada caminho que você adiciona é uma "observação".

Essas informações também podem ser encontradas em /proc, com os caminhos /proc/sys/fs/inotify/max_user_watches e /proc/sys/fs/inotify/max_user_instances.

Para aumentá-los, você pode usar sysctl ou escrever os valores no arquivo proc:

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

Para que as alterações tenham efeito após uma reinicialização, edite /etc/sysctl.conf ou /usr/lib/sysctl.d/50-default.conf (os detalhes podem variar para cada distribuição Linux, consulte a documentação da sua distribuição).

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

Alcançar o limite resultará em erros "No space left on device" ou "Too many open files".

kqueue (macOS, todos os sistemas BSD)

O kqueue exige a abertura de um descritor de arquivo para cada arquivo observado; portanto, se estiver observando um diretório contendo cinco arquivos, serão necessários seis descritores de arquivo. Nestas plataformas, você alcançará mais rapidamente o limite de "máximo de arquivos abertos" do sistema.

Você pode usar as variáveis de sysctl kern.maxfiles e kern.maxfilesperproc para controlar o número máximo de arquivos abertos.