質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

4047閲覧

logファイルの監視

ookura

総合スコア27

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/02/17 15:27

ログファイルを監視して、エラー文を発見したらbeep音+60秒経過でメール送信というプログラムを組んでいます。
watchdogでフォルダ内のログファイル(.log拡張子)が更新された後、
ログファイルの中の最終行を抜き取って【エラー】という文字列があれば
・処理1;logファイルに更新がなければ10秒毎にbeep関数呼び出しのループ
・処理2:60秒間(6回)beep関数が呼び出されたらmail関数呼び出し
→logファイルに更新があればbeep関数呼び出しループから離脱
という処理を行いたいのですが、処理1~が組み方がいまいちわかりません。
【エラー】の文字列を発見した後の処理でループ?さらにその中でファイルの監視をする処理を挟む?のでしょうか?
組立のヒントをいただけたらありがたいです。
よろしくお願いします。

python

1import time 2import winsound 3import win32com.client 4import PySimpleGUI as sg 5import os 6import threading 7 8from watchdog.events import FileSystemEventHandler 9from watchdog.observers import Observer 10 11def tail(fn, n, cnt): 12 # ファイルを開いてすべての行をリストで取得する 13 with open(fn, 'r', encoding="utf-8") as f: 14 # 一行読む. 一行目はヘッダーだから結果は捨てる 15 f.readline() 16 # 全行読む 17 lines = f.readlines() 18 # 後ろからn行だけ返す 19 if ("【エラー】" in str(lines[-n:])) == True: 20 threading.Timer(10,beep(2000, 800)).start() 21 #処理1;logファイルに更新がなければ10秒毎にbeep関数呼び出し 22 # 処理2:60秒間(6回)beep関数が呼び出されたらmail関数呼び出し 23 24def beep(freq, dur=100): 25 """ 26 ビープ音を鳴らす. 27 @param freq 周波数 28 @param dur 継続時間(ms) 29 """ 30 # Windowsの場合は、winsoundというPython標準ライブラリを使います. 31 32 winsound.Beep(freq, dur) 33def mail(fn, error): 34 outlook = win32com.client.Dispatch("Outlook.Application") 35 36 mail = outlook.CreateItem(0) 37 38 mail.to = '宛先1' 39 # mail.cc = #cc1 40 # mail.bcc = #bcc1 41 mail.subject = 'test' 42 mail.Attachments.Add(fn) 43 mail.bodyFormat = 1 44 mail.body = '''<自動送信メール> 45 プログラム実行中にエラーが発生しました。 46 エラー内容は下記の通りです。 47 ''' + error 48 49 mail.Send() 50 print("メール送信完了") 51 52# イベントハンドラ 53class ChangeHandler(FileSystemEventHandler): 54 55 # ファイルやフォルダが作成された場合 56 def on_created(self, event): 57 filepath = event.src_path 58 filename = os.path.basename(filepath) 59 print('%sを作成しました。' % filename) 60 61 # ファイルやフォルダが更新された場合 62 def on_modified(self, event): 63 filepath = event.src_path 64 filename = os.path.basename(filepath) 65 cnt,lines = tail(filepath, 1, 0) 66 window['-Multiline-'].print('%s :was changed.' % filepath, text_color='black') 67 window['-Multiline-'].print(lines, text_color='black') 68 #print('%s :was changed.' % filepath) 69 70 # ファイルやフォルダが移動された場合 71 def on_moved(self, event): 72 filepath = event.src_path 73 filename = os.path.basename(filepath) 74 print('%sを移動しました。' % filename) 75 76 # ファイルやフォルダが削除された場合 77 def on_deleted(self, event): 78 print(event) 79 filepath = event.src_path 80 print(filepath) 81 filename = os.path.basename(filepath) 82 print(filename) 83 print('%sを削除しました。' % filename) 84 85# 結果 86 87folder_path = r"C:\Users\ookura\Desktop\testfolder" 88 89# インスタンス作成 90event_handler = ChangeHandler() 91observer = Observer() 92 93# フォルダの監視 94observer.schedule(event_handler,folder_path, recursive=True) 95 96# 監視の開始 97observer.start() 98 99cnt = 0 100 101 102 103layout = [ 104#[sg.T("test")], 105[sg.Multiline(size=(30,10),background_color="ivory", border_width=0, key='-Multiline-',autoscroll=True,auto_refresh=True)], 106[sg.Text("exit",enable_events=True,key='exit')], 107] 108window = sg.Window('test',layout,background_color="ivory",no_titlebar=True,alpha_channel=0.5,font='メイリオ',keep_on_top=True, border_depth=0) 109try: 110 while True: 111 event,values = window.read(timeout=10) 112 113 if event is None: 114 print('exit') 115 break 116 if event == "exit": 117 break 118 119 120except KeyboardInterrupt: 121 122 # 監視の終了 123 observer.stop() 124 125 # スレッド停止を待つ 126 observer.join() 127 128 # 終了ログ 129 print('フォルダ・ファイル監視スクリプトを終了します。') 130 131

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/02/17 16:10 編集

以下のシナリオの場合、どのような動作となるべきなのでしょうか? [1] watchdogがログファイル(.log拡張子)が更新されたことを検知した ↓ [2] 更新されたログファイルの中の最終行を抜き取ったところ、【エラー】という文字列が含まれていた ↓ [3] 処理1(「その後のファイル更新を監視し、更新がなければ10秒間ごとにbeepする(6回beepするまでに更新があれば離脱する)」ループ処理)のループ開始 ここで、[3]のファイル監視中、logファイルが更新されたとします。 その更新内容が、「末尾に新たに【エラー】という文字を含む行が追加される」という内容(これをイベントAとします)であった場合、以下のいずれの動作となるべきですか? ・[1]~[3]を繰り返す (→この場合[3]の「6回beepするまで」の条件は前から引き継ぐのか?リセットされるのか?) ・beep関数呼び出しループから離脱し、次に更新されたときまで、[1]~[3]は処理しない(=イベントAは無視するということ) ・[3]の監視中に「末尾に新たに【エラー】という文字を含む行が追加される」という更新は絶対に起こりえない。 ・その他
ookura

2021/02/17 22:22

ご質問ありがとうございます。 ファイル更新があり、末尾が再度【エラー】の場合、1番目の選択肢の [1]~[3]を繰り返す (→この場合[3]の「6回beepするまで」の条件はリセットされる)という動作になります。
guest

回答1

0

ベストアンサー

自分なら、タイマー処理用のスレッドをプログラム開始時に1つ生成し、定期的に行う処理は全部それに任せます。排他処理を考える必要がありますがとりあえずグローバル変数で処理イメージを書くと;

python

1import os 2import time 3import threading 4 5flag = 0 # global変数 ログにエラーがあった時刻 6 7# タイマースレッド 8def timer_thread(): 9 print("thread開始") 10 prev_flag = 0 11 beep_count = 0 12 13 while True: 14 time.sleep(1) 15 16 if flag > 0: 17 if flag != prev_flag: # 新しいエラーが発生していたら 18 prev_flag = flag 19 beep_count = 0 20 continue 21 22 if time.time() > (prev_flag + beep_count * 10): 23 print("BEEP") 24 beep_count += 1 25 26 if beep_count == 6: 27 print("MAIL") 28 29 30# ログチェックに相当する処理(とりあえず"log.txt"の有無で代替) 31def log_check(): 32 global flag 33 if not os.path.exists("log.txt"): # ファイルをチェック 34 flag = 0 35 else: 36 if flag == 0: # 上書き防止するため flagをチェック 37 flag = time.time() # 質問者は上書きする仕様なのでこれは暫定措置 38 39 40th = threading.Thread(target=timer_thread) 41th.start() 42while True: 43 log_check() 44 time.sleep(2)

投稿2021/02/28 02:38

編集2021/03/09 12:57
sigsegv

総合スコア895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ookura

2021/03/09 05:31

ご回答ありがとうございます。 丁寧にご説明いただき、どうすればいいかが見えてきました。 ちなみに、sleep操作について、GUI画面の固まりを防ぐ為、sleep処理以外の方法で beepを行いたいのですが、その場合タイマー処理用スレッド内部で定期的に経過時間のカウントを行ってbeepさせるのが最良でしょうか? お手数ですがご教示いただけますと幸いです。
sigsegv

2021/03/09 13:03 編集

sleep は GUI を固まらせないために入れてるので、gui が固まるというのは「スレッドを生成」というところが伝わってないような気がします。 回答に書いたコードをもう少し具体的に書きなおしました。 ログファイルからエラー文を探す代わりに、カレントディレクトリに log.txt というファイルが存在すれば、フラグをセットするようにしています。log.txt が存在すると、10秒ごとに BEEP を表示し、6回目で MAIL と表示します。 > 定期的に経過時間のカウントを行ってbeepさせるのが最良でしょうか? 定期的に関数を呼び出しする方法でも可能です。 その場合はカウントを覚えるグローバル変数なども必要になると思います。
ookura

2021/03/11 08:25

連絡が遅くなりまして申し訳ありません。 ご教示いただいた内容で完全に欲しい動作が行えました。 スレッド生成について、知識不足だったのですが おかげ様で新しく覚えることができました!ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問