kakts-log

programming について調べたことを整理していきます

Golangでファイル監視を行う

概要

Goでファイル監視の方法についての記事です。
Goの標準パッケージではファイル監視の機能は提供されていないが、 github.com/fsnotify/fsnotify を使ったファイル監視のやり方をまとめます。

fsnotifyについて

github.com/fsnotify/fsnotify
各プラットフォーム・OSに対応したファイル変更通知機能(inotifyシステムコールなど)を提供するライブラリです。

主な使い方

ざっくり整理すると、以下の流れになります。

  • ファイル監視用のwatcherを作成する。
  • watcher.Addにより監視対象とするパスを追加
    1. ディレクトリを指定した場合: その配下のファイル全てが監視対象となる
    2. 特定のファイルを指定した場合: 複数のツールがファイルをアトミックに更新するため、一般的に推奨されない この場合、特定のファイルのみを監視対象とする場合、1のディレクトリ指定を行い、ファイル変更イベントが発生した際に、Event.Nameに変更があったファイルのパスが入るため、それをチェックして処理を行うのが推奨されます。
  • watcherがEvents, Errorsというチャネルを持っており、特定のファイルに対するイベントが発生した場合、チャネルにメッセージが送信されるのでそれを受信して処理を行う。

下記から、指定したディレクトリ配下の特定のファイルへのイベントを監視するコードについて説明します。

特定のファイルのみ更新したい場合

上記にあるように、watcher.Addで特定のファイルを指定するのでなく、そのファイルが属するディレクトリを指定し、Eventを監視し、Event.Nameをチェックして必要なファイルのみフィルタリングしてください。

./tmp ディレクトリ配下のtarget.txt への変更があった場合にEventを表示させる例を示します。

// 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 {
                    log.Println("watcher.Events is not ok")
                    return
                }

                // 特定のファイルへイベントが発生した場合
                if event.Name == "tmp/target.txt" {
                    log.Println("Event: ", event)
                } else {
                    log.Println("not target file", event.Name)
                }

            case err, ok := <-watcher.Errors:
                if !ok {
                    log.Println("watcher.Errors is not ok")
                    return
                }
                log.Println("error:", err)
            }
        }
    }()

    err = watcher.Add("./tmp")
    if err != nil {
        log.Fatal(err)
    }
    <-make(chan struct{})
}

case event, ok := <-watcher.Events: でwatcher.Eventsのchannelからのメッセージを受信した際の処理で、eventを受けとるので、ここからevent.Nameをチェックしています。

といった感じで指定したファイルへの変更イベントを拾うことができました。