pythonでのエラー処理機能について

# 背景
pythonでdfをfor文で扱うことが多く、デバッグ用にログを残すために下記を実現したく調査した。
- エラーが出ても全dfの処理を行う
- ログ名には、上書きされないようにタイムスタンプを付与したい
- エラー処理で終了しても、正常処理時と同様の処理を行いたい
- エラーはログに出力する。INFOログも残す

# それぞれの解決策
- エラーが出ても全dfの処理を行う  
⇒ try-exceptを使用
- エラーはログに出力する。INFOログもの残す 
⇒ datetimeを使用して付与したファイル名をlogging.basicConfigで設定
- エラー処理で終了しても、正常処理時と同様の処理を行いたい
⇒ finallyを使用
⇒ loggingモジュールのlogging.exceptionを使用。ログレベルをINFOにする。
- ログ名には、上書きされないようにタイムスタンプを付与したい
 
# 試しに作成したコード（err_test.py）
~~~python3
import logging
import pandas as pd
import matplotlib.pyplot as plt
import os
import datetime

# ログファイル名に実行ファイル名と日時を追加する
def get_log_filename():
    # 実行ファイル名を取得
    script_name = os.path.splitext(os.path.basename(__file__))[0]
    # 日時を取得
    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    # ログファイル名を作成
    log_filename = f"{script_name}_{timestamp}.log"
    return log_filename

# ログ設定
logging.basicConfig(filename=get_log_filename(), format='%(levelname)s:%(message)s', level=logging.INFO)
fname_list = ["test1.csv", "test2.csv", "test3.csv"]
result = True
for fname in  fname_list:
    try:
        logging.info(f"{fname}のグラフ化処理を実行します")
        df = pd.read_csv(fname)
        plt.plot(df["a"], df["b"])  # A列をx軸、B列をy軸としてプロット
        plt.show()  # グラフ表示
        result = True
    except Exception as e:
        logging.exception(f"{e}")
        result = False
    finally:
        plt.close()
        logging.info(f"{fname}のグラフ化処理が完了しました({result})")
~~~

出力したエラーファイル例：err_test_20241029225500.log
※test2.csvだけ”b”の列が存在しないように作成した
~~~
INFO:test1.csvのグラフ化処理を実行します
INFO:test1.csvのグラフ化処理が完了しました(True)
INFO:test2.csvのグラフ化処理を実行します
ERROR:'b'
Traceback (most recent call last):
  File "/home/shou10254/y311/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 3805, in get_loc
    return self._engine.get_loc(casted_key)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "index.pyx", line 167, in pandas._libs.index.IndexEngine.get_loc
  File "index.pyx", line 196, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 7081, in pandas._libs.hashtable.PyObjectHashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 7089, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 'b'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/shou10254/study/err_test.py", line 25, in <module>
    plt.plot(df["a"], df["b"])  # A列をx軸、B列をy軸としてプロット
                      ~~^^^^^
  File "/home/shou10254/y311/lib/python3.11/site-packages/pandas/core/frame.py", line 4102, in __getitem__
    indexer = self.columns.get_loc(key)
              ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/shou10254/y311/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 3812, in get_loc
    raise KeyError(key) from err
KeyError: 'b'
INFO:test2.csvのグラフ化処理が完了しました(False)
INFO:test3.csvのグラフ化処理を実行します
INFO:test3.csvのグラフ化処理が完了しました(True)
~~~

# デバッグの効率化
## 問題発生箇所の特定  
エラー発生時のスタックトレースや変数の状態などがログとして残るため、問題の原因究明をスムーズに行えます。

## デバッグ作業の効率UP  
print文デバッグと違い、ログレベルを調整することで、必要な情報だけを効率的に確認できます。

# print文デバッグとの違い

## ログレベルによる出力制御  
loggingでは、DEBUG, INFO, WARNING, ERROR, CRITICALなど、ログレベルを設定できる。状況に応じて出力内容を制御することで、見たい情報だけを効率的に確認可能。

## 出力先の柔軟性  
loggingでは、ファイル、コンソール、データベースなど、ログの出力先を自由に設定可能。状況に応じて使い分けることで、ログ管理を効率化可能。

## フォーマットの統一  
loggingでは、ログの出力形式を統一可能。これにより、ログ解析ツールとの連携や自動処理が容易になる。

# 参考url
https://docs.python.org/ja/3/howto/logging.html