Pythonの例外処理のエラー内容をLINEに通知する方法

我が家ではRaspberry Piを常時起動してPythonでTwitterのBotを運用したりしています。
問題となるのは、突然のPythonの例外処理などのエラーでコードが正常に実行できなかったときに、それを瞬時に知る手段がないことです。
そのため、我が家ではエラーが起きた瞬間にLINEに通知ようにしています。
また、エラー文が長すぎる場合はPastebinに投稿して、そのURLが通知されるようにしています。
また、万が一通信エラーが生じた場合には、一度LOGに記録し、再起動するようにしています。

例えば`hoge.py`が以下のようなコードであったとします。

```Python:hoge.py
print(hoge)
```
エラーは以下のように出ます。

```
Traceback (most recent call last):
  File "hoge.py", line 1, in <module>
    print(hoge)
NameError: name 'hoge' is not defined
```
`hoge`が定義されていないと怒られます。当然ですね。

これがLINEに通知されるようにします。

# 実装

まず`hoge.py`を以下のようにします。

```Python:hoge.py
import traceback

import notify_traceback

try:
    print(hoge)
except Exception:
    notify_traceback.send(traceback.format_exc())
```
例外処理が起きたときに通知してほしい箇所（今回では`print(hoge)`）をtry・except文で囲います。
tryの中で起きた例外処理をexceptでキャッチし、予めインポートした`notify_traceback`というライブラリにtraceback内容を渡します。


そして、`notify_traceback.py`というライブラリを作成します。これをPythonのライブラリに入れたり、`hoge.py`と同じディレクトリに入れます。

```Python:notify_traceback.py
import datetime
import os
import requests

import line_notify
from pbwrap import Pastebin

def send(traceback, fail_datetime=None):
    traceback = traceback.rstrip()
    
    print(traceback)
    
    try:
        if len(traceback) > 1000:
            PASTEBIN_API_KEY = ###ここにPastebinのアクセストークン###
            PASTE_NAME = str(datetime.datetime.now().replace(microsecond=0))
            
            pb = Pastebin(PASTEBIN_API_KEY)
            
            url = pb.create_paste(traceback, api_paste_private=1, api_paste_name=PASTE_NAME, api_paste_expire_date='1W')
            raw_url = url.replace('com/', 'com/raw/', 1)
            message = raw_url
        else:
            message = '\n' + traceback
            
        if fail_datetime:
            message = f'{fail_datetime}:\n{message}'
    
        LINE_NOTIFY_ACCESS_TOKEN = ###ここにLINE Notifyのアクセストークン###
    
        client = line_notify.LineNotify(token=LINE_NOTIFY_ACCESS_TOKEN)
        res = client.notify(message, time_format=None)
        print(res[0])
    
    except requests.exceptions.ConnectionError:
        ERROR_FAIL_TEXT_PATH = ###万が一送信できなかった場合のLOGのパス###
        
        with open(ERROR_FAIL_TEXT_PATH, mode='w') as f:
            f.write(traceback)
        os.system('sudo reboot')
        
if __name__ == '__main__':
    send(f'Traceback (most recent call last):\n  File {__file__}, line ∞, in <module>\nAbsolutelyZeroErrors: it\'s just fine')
```

なお、pypiからpyline-notifyをインストールしておく必要があります。

```
pip install pyline-notify
```
LINE通知を使用するためのアクセストークンも必要です。[公式サイト](https://notify-bot.line.me/ja/)、もしくはググってください。

また、pypiからpbwrapもインストールしておく必要があります。
```
pip install pbwrap
```

これについても、Pastebinのアクセストークンも必要です。ログインしてからAPIの[公式サイト](https://pastebin.com/doc_api)から取得可能です。

# 結果
これで`hoge.py`を実行すると、以下のようにLINEに通知が来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567033/c33cdde8-495e-a026-125c-fb7c37fd20ff.png)

また、エラーが長すぎる場合はこうなります。
![mosaic_20220331200201.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567033/f76bccf8-89dc-3608-ddb8-7f25a7e8fefe.png)
渡しの場合、リンクは1週間保持されるようにしましたが、これは設定可能です。

これで、Rasbperry Piに何らかのエラーが起きてもすぐにどんなエラーが起きたか確認できます。

# 応用
今回はLINEで知らせるようにしましたが、メールで知らせたり、Twitterでツイートにするようにしたりもできますね。