fsnotify - это библиотека уведомлений файловой системы, написанная на Go, которая может отслеживать изменения файлов и каталогов в файловой системе и уведомлять приложение о возникших изменениях. Она поддерживает кросс-платформенную работу и может работать на различных операционных системах, таких как Linux, macOS и Windows. fsnotify использует механизмы уведомлений файловой системы различных операционных систем, такие как inotify в Linux, FSEvents в macOS и ReadDirectoryChangesW в Windows.
Требуется использование Go версии 1.17 или более поздней; полная документация может быть найдена на https://pkg.go.dev/github.com/fsnotify/fsnotify
Поддержка различных операционных систем:
Backend | Операционная система | Статус |
---|---|---|
inotify | Linux | Поддерживается |
kqueue | BSD, macOS | Поддерживается |
ReadDirectoryChangesW | Windows | Поддерживается |
FEN | illumos | Поддерживается |
fanotify | Linux 5.9+ | Пока не поддерживается |
AHAFS | AIX | Ветка AIX; Экспериментальная функция из-за отсутствия ведущих и тестовых сред |
FSEvents | macOS | Требуется поддержка x/sys/unix |
USN Journals | Windows | Требуется поддержка x/sys/windows |
Polling | Все | Пока не поддерживается |
Android и Solaris, которые должны включать Linux и illumos, пока не были протестированы.
Примеры использования
Примеры использования fsnotify включают, но не ограничиваются следующими ситуациями:
- Синхронизация файлов в реальном времени: fsnotify может отслеживать изменения в файловой системе в реальном времени, что делает его подходящим для реализации синхронизации файлов в реальном времени. Когда происходят изменения в исходном файле, изменения могут немедленно синхронизироваться с целевым файлом, обеспечивая согласованность файлов.
- Автоматизированная сборка: fsnotify может отслеживать изменения в исходном коде и файлов зависимостей проекта, запуская команды сборки при возникновении изменений, тем самым достигая автоматизированной сборки. Это позволяет экономить время и усилия, затрачиваемые на ручную сборку, и повышает эффективность разработки.
- Резервное копирование файлов: fsnotify может отслеживать изменения в файлах или каталогах, которые нужно резервировать, и сразу же начинать резервное копирование при возникновении изменений. Это обеспечивает безопасность данных и предотвращает потерю данных из-за потери или повреждения файлов.
- Мониторинг логов в реальном времени: fsnotify может отслеживать операции, такие как создание, изменение и удаление лог-файлов, запуская программы мониторинга логов при изменении в лог-файлах, что позволяет эффективно отслеживать изменения в содержании логов в реальном времени.
- Мониторинг безопасности файловой системы: fsnotify может отслеживать события безопасности в файловой системе, такие как доступ к файлам, изменение и удаление. Это позволяет мониторить безопасность файловой системы, своевременно выявляя и регистрируя небезопасные события.
Пример использования
Базовый пример:
package main
import (
"log"
"github.com/fsnotify/fsnotify"
)
func main() {
// Создаем новый наблюдатель.
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
// Начинаем слушать события.
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("Событие:", event)
if event.Has(fsnotify.Write) {
log.Println("Измененный файл:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("Ошибка:", err)
}
}
}()
// Добавляем путь, который нужно отслеживать.
err = watcher.Add("/tmp")
if err != nil {
log.Fatal(err)
}
// Блокируем основную горутину.
<-make(chan struct{})
}
Более подробные примеры можно найти в cmd/fsnotify и запустить с помощью следующей команды:
% go run ./cmd/fsnotify
Более подробная документация доступна в godoc: https://pkg.go.dev/github.com/fsnotify/fsnotify
Часто задаваемые вопросы
Будет ли осуществляться мониторинг, если файл перемещен в другой каталог?
Нет, если вы не отслеживаете расположение, куда он был перемещен.
Он отслеживает подкаталоги?
Нет, вы должны добавить отслеживание для каждого каталога, который вы хотите отслеживать (рекурсивное отслеживание запланировано: #18).
Нужно ли одновременно отслеживать как ошибки, так и события в горутине?
Да. Вы можете использовать select
для чтения из обоих каналов в той же горутине (вам не нужно запускать горутину отдельно для каждого канала; см. пример).
Почему уведомления не работают в NFS, SMB, FUSE, /proc или /sys?
Для работы fsnotify требуется поддержка со стороны операционной системы. В текущих протоколах NFS и SMB нет поддержки уведомлений о файлах на уровне сети, а также виртуальные файловые системы /proc и /sys также не предоставляют поддержки.
Это можно исправить, используя наблюдение через опрос (см. #9), но это пока не было реализовано.
Почему я получаю много событий Chmod?
Некоторые программы могут генерировать большое количество изменений атрибутов, таких как Spotlight на macOS, антивирусные программы, резервные копии и некоторые другие известные приложения. В целом, игнорирование событий Chmod обычно является лучшей практикой, так как они часто бесполезны и могут вызывать проблемы.
Индексирование Spotlight на macOS может вызывать несколько событий (см. #15). Временным решением является добавление ваших папок в Настройки конфиденциальности Spotlight, пока не будет реализовано нативное использование FSEvents (см. #11).
Отслеживание файлов плохо работает
Обычно не рекомендуется отслеживать отдельные файлы (а не каталоги), потому что многие программы, особенно редакторы, атомарно обновляют файлы: сначала они записывают во временный файл, а затем перемещают его в целевое местоположение, заменяя исходный файл (или его вариант). Отслеживание исходного файла теперь потеряно, потому что файла больше не существует.
В результате сбоя питания или аварии не будет иметь недописанный файл.
Отслеживайте родительский каталог и используйте Event.Name
для фильтрации файлов, которые вас не интересуют. В файле cmd/fsnotify/file.go
есть пример.
Примечания для конкретных платформ
Linux
При удалении файла событие REMOVE не будет выдано до тех пор, пока все дескрипторы файлов не будут закрыты; в этот момент будет выдано событие CHMOD:
fp := os.Open("file")
os.Remove("file") // CHMOD
fp.Close() // REMOVE
Это событие, отправляемое системой inotify, поэтому в нем не должно быть слишком много изменений.
Переменная sysctl fs.inotify.max_user_watches
указывает максимальное количество наблюдений на пользователя, а fs.inotify.max_user_instances
указывает максимальное количество экземпляров inotify на пользователя. Каждый созданный вами Watcher - это "экземпляр", а каждый добавленный вами путь - это "наблюдение".
Эти переменные также можно найти в /proc
по путям /proc/sys/fs/inotify/max_user_watches
и /proc/sys/fs/inotify/max_user_instances
.
Чтобы увеличить их, вы можете использовать sysctl
или записать значения в файл proc:
sysctl fs.inotify.max_user_watches=124983
sysctl fs.inotify.max_user_instances=128
Чтобы изменения вступили в силу после перезагрузки, отредактируйте /etc/sysctl.conf
или /usr/lib/sysctl.d/50-default.conf
(детали могут отличаться для каждого дистрибутива Linux, пожалуйста, обратитесь к документации вашего дистрибутива):
fs.inotify.max_user_watches=124983
fs.inotify.max_user_instances=128
Достижение лимита приведет к ошибкам "Нет свободного места на устройстве" или "Слишком много открытых файлов".
kqueue (macOS, все системы BSD)
Для kqueue требуется открытие дескриптора файла для каждого наблюдаемого файла; поэтому, если вы наблюдаете каталог, содержащий пять файлов, это будет шесть дескрипторов файлов. На этих платформах вы быстрее достигнете предела "максимальное количество открытых файлов" системы.
Вы можете использовать переменные sysctl kern.maxfiles
и kern.maxfilesperproc
, чтобы контролировать максимальное количество открытых файлов.