はじめに
初めましての人もそうでない人もこんにちは!
最近コーディング中に「あれ、いつこのファイル変更したっけ?」「どの部分を修正したか忘れちゃった...」なんて経験ありませんか?
そんな悩みを解決するべく、今回はPythonでファイルの変更をリアルタイムで監視して、詳細なログを出力するツールを作成してみました!
ファイルの作成・削除・変更を文字単位で追跡できる超便利なツールになっているので、ぜひ最後までご覧ください!
今回作るもの
- ファイルの作成・削除・変更をリアルタイムで監視
- 文字の追加・削除・置換を詳細にログ出力
- CSVファイルで変更履歴を管理
- 対応ファイル形式:
.py
,.txt
,.js
,.html
,.css
,.json
ディレクトリ構成
/
│
├── file_monitor.py # メインのプログラム
├── logs/
│ └── logs.csv # ログ収集用ファイル(自動生成)
└── files/ # 監視対象フォルダ(自動生成)
├── hogehoge.py
├── hogehoge.txt
└── ...
環境構築
必要なライブラリをインストールします:
pip install watchdog pytz
コーディング
メインのプログラムファイル file_monitor.py
を作成します:
import csv
import os
from datetime import datetime
import pytz
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class FileChangeHandler(FileSystemEventHandler):
def __init__(self, logger):
self.logger = logger
self.file_contents = {}
def on_modified(self, event):
if not event.is_directory and event.src_path.endswith(('.py', '.txt', '.js', '.html', '.css', '.json')):
filename = os.path.basename(event.src_path)
try:
with open(event.src_path, 'r', encoding='utf-8') as f:
new_content = f.read()
old_content = self.file_contents.get(event.src_path, '')
if old_content != new_content:
self.compare_content(filename, old_content, new_content)
self.file_contents[event.src_path] = new_content
except:
pass
def compare_content(self, filename, old_content, new_content):
if len(new_content) > len(old_content):
added_text = new_content[len(old_content):]
line_num, col_num = self.get_position(old_content, len(old_content))
for i, char in enumerate(added_text):
if char == '\n':
self.logger.log(filename, '入力', "追加: '改行'", line_num, col_num + i + 1)
line_num += 1
col_num = 0
i = -1
else:
self.logger.log(filename, '入力', f"追加: '{char}'", line_num, col_num + i + 1)
elif len(new_content) < len(old_content):
deleted_text = old_content[len(new_content):]
line_num, col_num = self.get_position(new_content, len(new_content))
for char in deleted_text:
self.logger.log(filename, '削除', f"削除: '{char}'" if char != '\n' else "削除: '改行'", line_num, col_num + 1)
else:
for i, (old_char, new_char) in enumerate(zip(old_content, new_content)):
if old_char != new_char:
line_num, col_num = self.get_position(old_content, i)
self.logger.log(filename, '置換', f"'{old_char}' → '{new_char}'", line_num, col_num)
def get_position(self, content, index):
if index >= len(content):
lines = content.split('\n')
return len(lines), len(lines[-1]) + 1 if lines else 1
text_before = content[:index]
lines = text_before.split('\n')
return len(lines), len(lines[-1]) + 1
def on_created(self, event):
if not event.is_directory:
self.logger.log(os.path.basename(event.src_path), 'ファイル作成', f"ファイル作成: {os.path.basename(event.src_path)}", 0, 0)
try:
with open(event.src_path, 'r', encoding='utf-8') as f:
self.file_contents[event.src_path] = f.read()
except:
pass
def on_deleted(self, event):
if not event.is_directory:
self.logger.log(os.path.basename(event.src_path), 'ファイル削除', f"ファイル削除: {os.path.basename(event.src_path)}", 0, 0)
self.file_contents.pop(event.src_path, None)
class FileLogger:
def __init__(self):
os.makedirs("logs", exist_ok=True)
os.makedirs("files", exist_ok=True)
with open("logs/logs.csv", 'w', newline='', encoding='utf-8') as f:
csv.writer(f).writerow(['時刻', 'ファイル名', '操作', '内容', '行', '列'])
def log(self, filename, action, content, line=0, column=0):
timestamp = datetime.now(pytz.timezone('Asia/Tokyo')).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
with open("logs/logs.csv", 'a', newline='', encoding='utf-8') as f:
csv.writer(f).writerow([timestamp, filename, action, content, line, column])
def start_monitor(self):
event_handler = FileChangeHandler(self)
for root, dirs, files in os.walk("files"):
for file in files:
if file.endswith(('.py', '.txt', '.js', '.html', '.css', '.json')):
filepath = os.path.join(root, file)
try:
with open(filepath, 'r', encoding='utf-8') as f:
event_handler.file_contents[filepath] = f.read()
except:
pass
observer = Observer()
observer.schedule(event_handler, "files", recursive=True)
observer.start()
print("ファイル監視を開始しました。Ctrl+Cで終了します。")
print("監視対象フォルダ: ./files/")
print("ログファイル: ./logs/logs.csv")
try:
import time
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
print("\nファイル監視を終了しました。")
observer.join()
if __name__ == "__main__":
FileLogger().start_monitor()
実行してみた
プログラムを実行します:
python3 file_monitor.py
実行すると以下のようなメッセージが表示されます:
ファイル監視を開始しました。Ctrl+Cで終了します。
監視対象フォルダ: ./files/
ログファイル: ./logs/logs.csv
files/
フォルダ内で以下のようなテストファイルを作成・編集してみました:
test.py を作成
print("Hello World")
sample.txt を編集
最初のテキスト
追加のテキスト
ログ出力結果
logs/logs.csv
に以下のようなログが出力されました:
時刻 | ファイル名 | 操作 | 内容 | 行 | 列 |
---|---|---|---|---|---|
2025-05-30 15:30:01.123 | test.py | ファイル作成 | ファイル作成: test.py | 0 | 0 |
2025-05-30 15:30:15.456 | test.py | 入力 | 追加: 'p' | 1 | 1 |
2025-05-30 15:30:15.457 | test.py | 入力 | 追加: 'r' | 1 | 2 |
2025-05-30 15:30:15.458 | test.py | 入力 | 追加: 'i' | 1 | 3 |
... | ... | ... | ... | ... | ... |
特徴・機能
リアルタイム監視
- ファイルの変更を瞬時に検知
- 複数ファイルの同時監視が可能
詳細なログ記録
- 文字単位での変更追跡
- 行番号・列番号の正確な位置情報
- 時系列管理
多くの操作ログの収集に対応
- 文字追加: 新しく入力された文字を記録
- 文字削除: 削除された文字を記録
- 文字置換: 変更前後の文字を記録
- ファイル作成/削除: ファイル操作も記録
おわりに
今回は Python と watchdog ライブラリを使って、ファイル変更をリアルタイムで監視するツールを作成しました!
文字単位での詳細な変更追跡ができるので、コーディング中の変更履歴をしっかりと記録できます。
開発効率の向上やデバッグ支援にぜひ活用してみてください!
皆さんも試してみてはいかがでしょうか?
今回の記事はいかがだったでしょうか!
またどこかの記事でお会いしましょう!