ログ監視 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もそのうち使うかも? 今回は関係ないけど