aoirint's note

メモ帳

ログ監視 Python watchdog(ログローテーション未完成)

アプリケーションのログファイルを監視するシステムをつくる。

上の2つをがっちゃんこしたやつを作った。ファイルの変更監視はwatchdog、読み取りはふつうのIO。

※ うーん、ログローテーション対応が微妙かも

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import time
import os
from stat import *

class FileWatchHandler(FileSystemEventHandler):
    def __init__(self, path):
        self.path = os.path.realpath(path)
        self.fp = None
        self.fpos = None

        self.init()

        filesize = os.stat(self.path)[ST_SIZE]
        self.fp.seek(filesize) # ファイル末尾
        self.fpos = filesize

    def init(self):
        fp = self.fp
        if fp:
            fp.close()
        fp = open(self.path, 'r')

        self.fp = fp
        self.fpos = None

    def on_created(self, event):
        if event.src_path == self.path:
            print('reset')
            self.init()

    def on_modified(self, event):
        if event.src_path == self.path:
            print('modified')
            self.tail_f()

    def tail_f(self):
        if self.fpos:
            self.fp.seek(self.fpos)

        while True:
            self.fpos = self.fp.tell()
            line = self.fp.readline()

            if not line:
                break
            self.analyze(line)

    def analyze(self, line):
        # TODO:
        print('!', line)

    def close(self):
        self.fp.close()

path = 'test.txt'

handler = FileWatchHandler(path)
observer = Observer()
observer.schedule(handler, os.path.dirname(os.path.realpath(path)))
observer.start()

try:
    observer.join()
except KeyboardInterrupt:
    pass

observer.stop()
handler.close()

print('closed')

※ ちょっと修正してみたけど、やっぱり微妙

    def tail_f(self):
        filesize = os.stat(self.path)[ST_SIZE]
        if self.fpos:
            if self.fpos <= filesize:
                self.fp.seek(self.fpos)
            else:
                self.init()

        while True:
            self.fpos = self.fp.tell()
            line = self.fp.readline()
            
            if not line:
                break
            self.analyze(line)

(ログローテーションはともかく、)あとはanalyze関数に新しくappendされた行が入ってくるので、それぞれ解析すればOK。

ログは定期的に分割されて別ファイルに移動、新しいログが入ってきたら新規ファイルになるので、on_createdで開き直し(実はseekだけすればいいのかな?)。このとき、作成と同時に内容が書き込まれるとon_modifiedが落としちゃったのでファイル末尾への移動は入れてない。それからリネームでファイルがやってきても動かない(on_movedでいけるけど)。

observer.joinって、joinだし別スレッドの終了待ちだと思うので、これでいいかな、と思ったけど公式サンプル(GitHub - gorakhargosh/watchdog: Python library and shell utilities to monitor filesystem events.)は違う...。まあプロセス死んだらなんにせよ止まると願って。

末尾追従動作がけっこう面倒くさいので、すなおにtail -fをPopenすればよかったかな...

diffもそのうち使うかも? 今回は関係ないけど