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:
- 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.
- 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.
- 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.
- 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.
- 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.